From 3521912f448acd2535d50cc0066c378e589e3955 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 7 Feb 2024 18:53:10 +0100 Subject: [PATCH 001/142] WIP: relational schema generation 1 --- cli/example_transactional.sql | 76 + cli/generate_sql_schema.py | 1247 +++++++++++ {models_validator => cli}/requirements.txt | 0 {models_validator => cli}/validate.py | 2 +- models.yml | 93 +- schema_relational.sql | 2350 ++++++++++++++++++++ 6 files changed, 3729 insertions(+), 39 deletions(-) create mode 100644 cli/example_transactional.sql create mode 100644 cli/generate_sql_schema.py rename {models_validator => cli}/requirements.txt (100%) rename {models_validator => cli}/validate.py (99%) create mode 100644 schema_relational.sql diff --git a/cli/example_transactional.sql b/cli/example_transactional.sql new file mode 100644 index 00000000..7a6a5516 --- /dev/null +++ b/cli/example_transactional.sql @@ -0,0 +1,76 @@ +-- this script can only be used for an empty database without used sequences +BEGIN; +INSERT INTO themeT (name, accent_500, primary_500, warn_500) values ('standard theme', 2201331, 3241878, 15754240) returning id as ret_theme_id; +INSERT INTO organizationT (name, theme_id, default_language) values ('Intevation', ret_theme_id, 'en'); + +INSERT INTO committeeT (name) VALUES ('c1'), ('c2'), ('c3'), ('c4'); +-- INSERT INTO forwarding_committee_to_committee values (1, 2), (1, 3), (4, 1); + +-- meeting1 in committee 1 with 2 groups, simple workflow and 1 reference projector +INSERT INTO meetingT (default_group_id, admin_group_id, + motions_default_workflow_id, motions_default_amendment_workflow_id, + motions_default_statute_amendment_workflow_id, + committee_id, reference_projector_id, name, language) + VALUES (1, 2, 1, 1, 1, 1, 1, 'meeting1', 'en'); +INSERT INTO groupT (name, meeting_id, permissions) + VALUES + ('Default', 1, '{ + "agenda_item.can_see", + "assignment.can_see", + "meeting.can_see_autopilot", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see" + }'), ('Admin', 1, DEFAULT); + +INSERT INTO motion_workflowT (name, sequential_number, first_state_id, meeting_id) + VALUES ('Simple Workflow', 1, 1, 1); +INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, allow_create_poll, allow_support, set_workflow_timestamp) + VALUES ('submitted', 1, 1, 1, true, true, true); +INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, + recommendation_label, css_class, merge_amendment_into_final) VALUES + ('accepted', 2, 1, 1, 'Acceptance', 'green', 'do_merge'), + ('rejected', 3, 1, 1, 'Rejection', 'red', 'do_not_merge'), + ('not decided', 4, 1, 1, 'No decision', 'grey', 'do_not_merge'); +-- INSERT INTO motion_state_to_stateT (previous_state_id, next_state_id) VALUES +-- (1, 2), (1, 3), (1,4); +INSERT INTO projectorT (name, sequential_number, meeting_id) VALUES ('Projektor 1', 1, 1); + +-- -- meeting2 in committee 2 with 2 groups, simple workflow and 1 reference projector +INSERT INTO meetingT (default_group_id, admin_group_id, + motions_default_workflow_id, motions_default_amendment_workflow_id, + motions_default_statute_amendment_workflow_id, + committee_id, reference_projector_id, name, language) + VALUES (3, 4, 2, 2, 2, 2, 2, 'meeting2', 'en'); +INSERT INTO groupT (name, meeting_id, permissions) + VALUES + ('Default', 2, '{ + "agenda_item.can_see_internal", + "list_of_speakers.can_see", + "mediafile.can_see", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see", + "user.can_see" + }'), ('Admin', 2, DEFAULT); +-- Update a specific cell +UPDATE groupt set permissions[3] = 'user.can_manage' where name = 'Default'; +INSERT INTO motion_workflowT (name, sequential_number, first_state_id, meeting_id) + VALUES ('Simple Workflow', 1, 5, 2); +INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, allow_create_poll, allow_support, set_workflow_timestamp) + VALUES ('submitted', 1, 2, 2, true, true, true); +INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, + recommendation_label, css_class, merge_amendment_into_final) VALUES + ('accepted', 2, 2, 2, 'Acceptance', 'green', 'do_merge'), + ('rejected', 3, 2, 2, 'Rejection', 'red', 'do_not_merge'), + ('not decided', 4, 2, 2, 'No decision', 'grey', 'do_not_merge'); +-- INSERT INTO motion_state_to_state (previous_state_id, next_state_id) VALUES +-- (5, 6), (5, 7), (8, 5); +INSERT INTO projectorT (name, sequential_number, meeting_id) VALUES ('Projektor 2', 1, 2); + +-- -- user 1 to 4 with relations to meetings and committes as managers +-- INSERT INTO userT (username) values ('u1_m1'), ('u2_m2'), ('u3_c1manager'), ('u4_m1m2c1managerc3manager'); +-- INSERT INTO group_to_user (user_id, group_id) values (1, 1), (2, 4), (4, 1), (4, 3); +-- INSERT INTO committee_to_user (user_id, committee_id) values (3, 1), (4, 1), (4,3); + +COMMIT; \ No newline at end of file diff --git a/cli/generate_sql_schema.py b/cli/generate_sql_schema.py new file mode 100644 index 00000000..e6ec893c --- /dev/null +++ b/cli/generate_sql_schema.py @@ -0,0 +1,1247 @@ +import hashlib +import os +import re +import string +import sys +from collections import defaultdict +from decimal import Decimal +from enum import Enum +from string import Formatter +from textwrap import dedent +from typing import (Any, Callable, Dict, List, Optional, Tuple, TypedDict, + Union, cast) + +import requests +import yaml + +SOURCE = "global/meta/models.yml" +DESTINATION = "global/meta/schema_relational.sql" +MODELS: Dict[str, Dict[str, Any]] = {} + + +class TableFieldType: + def __init__( + self, + table: str, + column: str, + field_def: Optional[Dict[str, Any]], + ref_column: str = "id", + ): + self.table = table + self.column = column + self.field_def: Dict[str, Any] = field_def or {} + self.ref_column = ref_column + + @property + def fqid(self) -> str: + if self.table: + return f"{self.table}/{self.column}" + else: + return "-" + + +class SchemaZoneTexts(TypedDict, total=False): + """TypedDict definition for generation of different sql-code parts""" + + table: str + view: str + alter_table: str + alter_table_final: str + undecided: str + final_info: str + + +class ToDict(TypedDict): + """Defines the dict keys for the to-Attribute of generic relations in field definitions""" + + collections: List[str] + field: str + + +class SQL_Delete_Update_Options(str, Enum): + RESTRICT = "RESTRICT" + CASCADE = "CASCADE" + SET_NULL = "SET NULL" + SET_DEFAULT = "SET DEFAULT" + NO_ACTION = "NO ACTION" + + +class SubstDict(TypedDict, total=False): + """dict for substitutions of field templates""" + + field_name: str + type: str + primary_key: str + required: str + default: str + minimum: str + minLength: str + enum_: str + + +class GenerateCodeBlocks: + """Main work is done here by recursing the models and their fields and determine the method to use""" + intermediate_tables: Dict[str, str] = {} # Key=Name, data: collected content of table + + @classmethod + def generate_the_code(cls) -> Tuple[str, str, str, str, str, List[str], str]: + """ + Return values: + pre_code: Type definitions etc., which should all appear before first table definitions + table_name_code: All table definitions + view_name_code: All view definitions, after all views, because of view field definition by sql + alter_table_final_code: Changes on tables defining relations after, which should appear after all table/views definition to be sequence independant + final_info_code: Detailed info about all relation fields.Types: relation, relation-list, generic-relation and generic-relation-list + missing_handled_atributes: List of unhandled attributes. handled one's are to be set manually. + im_table_code: Code for intermediate tables. + n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation + g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field + """ + handled_attributes = set( + ( + "required", + "maxLength", + "minLength", + "default", + "type", + "restriction_mode", + "minimum", + "calculated", + "description", + "read_only", + "enum", + "items", + "to", # will be used for creating view-fields, but also replacement for fk-reference to id + # "on_delete", # must have other name then the key-value-store one + # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? + # "unique", # still to design + ) + ) + pre_code: str = "" + table_name_code: str = "" + view_name_code: str = "" + alter_table_final_code: str = "" + final_info_code: str = "" + missing_handled_attributes = [] + im_table_code = "" + + for table_name, fields in MODELS.items(): + if table_name in ["_migration_index", "_meta"]: + continue + schema_zone_texts: SchemaZoneTexts = defaultdict(str) # type: ignore + cls.intermediate_tables = {} + # commented, because automatically expanded. Would be better to not expand automatically + # for reducing redundancy + # if table_name == "_meta": + # for enum_name, enum_data in fields.items(): + # pre_code += Helper.get_enum_type_definition( + # "", enum_name, enum_data + # ) + # continue + + for fname, fdata in fields.items(): + for attr in fdata: + if ( + attr not in handled_attributes + and attr not in missing_handled_attributes + ): + missing_handled_attributes.append(attr) + method_or_str, type_ = cls.get_method(fname, fdata) + if isinstance(method_or_str, str): + schema_zone_texts["undecided"] += method_or_str + else: + if (enum_ := fdata.get("enum")) or ( + enum_ := fdata.get("items", {}).get("enum") + ): + pre_code += Helper.get_enum_type_definition( + table_name, fname, enum_ + ) + result = method_or_str(table_name, fname, fdata, type_) + for k, v in result.items(): + schema_zone_texts[k] += v or "" # type: ignore + + if code := schema_zone_texts["table"]: + table_name_code += Helper.get_table_head(table_name) + table_name_code += Helper.get_table_body_end(code) + "\n\n" + if code := schema_zone_texts["alter_table"]: + table_name_code += code + "\n" + if code := schema_zone_texts["undecided"]: + table_name_code += Helper.get_undecided_all(table_name, code) + if code := schema_zone_texts["view"]: + view_name_code += Helper.get_view_head(table_name) + view_name_code += Helper.get_view_body_end(table_name, code) + if code := schema_zone_texts["alter_table_final"]: + alter_table_final_code += code + "\n" + if code := schema_zone_texts["final_info"]: + final_info_code += code + "\n" + for im_table in cls.intermediate_tables.values(): + im_table_code += im_table + + + return ( + pre_code, + table_name_code, + view_name_code, + alter_table_final_code, + final_info_code, + missing_handled_attributes, + im_table_code, + ) + + @classmethod + def get_method( + cls, fname: str, fdata: Dict[str, Any] + ) -> Tuple[Union[str, Callable[..., SchemaZoneTexts]], str]: + if fdata.get("calculated"): + return ( + f" {fname} type:{fdata.get('type')} is marked as a calculated field\n", + "", + ) + if fname == "id": + type_ = "primary_key" + return (FIELD_TYPES[type_].get("method", "").__get__(cls), type_) + if ( + (type_ := fdata.get("type", "")) + and type_ in FIELD_TYPES + and (method := FIELD_TYPES[type_].get("method")) + ): + return (method.__get__(cls), type_) # returns the callable classmethod + text = "no method defined" if type_ else "Unknown Type" + return (f" {fname} type:{fdata.get('type')} {text}\n", type_) + + @classmethod + def get_schema_simple_types( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts + subst, text = Helper.get_initials(table_name, fname, type_, fdata) + if isinstance((tmp := subst["type"]), string.Template): + if maxLength := fdata.get("maxLength"): + tmp = tmp.substitute({"maxLength": maxLength}) + elif isinstance(type_, Decimal): + tmp = tmp.substitute({"maxLength": 6}) + elif isinstance(type_, str): # string + tmp = tmp.substitute({"maxLength": 256}) + subst["type"] = tmp + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text + + @classmethod + def get_schema_color( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts + subst, text = Helper.get_initials(table_name, fname, type_, fdata) + tmpl = FIELD_TYPES[type_]["pg_type"] + subst["type"] = tmpl.substitute(subst) + if default := fdata.get("default"): + subst["default"] = f" DEFAULT {int(default[1:], 16)}" + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text + + @classmethod + def get_schema_primary_key( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts + subst, text = Helper.get_initials(table_name, fname, type_, fdata) + subst["primary_key"] = " PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY" + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text + + @classmethod + def get_relation_type( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts = {} + own_table_field = TableFieldType(table_name, fname, fdata) + foreign_table_field: TableFieldType = ModelsHelper.get_definitions_from_foreign( + fdata.get("to"), fdata.get("reference") + ) + final_info, error = Helper.check_relation_definitions( + own_table_field, [foreign_table_field] + ) + + if not error: + if result := Helper.generate_field_view_or_nothing( + own_table_field, foreign_table_field + ): + text.update( + cls.get_schema_simple_types(table_name, fname, fdata, "number") + ) + initially_deferred = ModelsHelper.is_fk_initially_deferred( + table_name, foreign_table_field.table + ) + text["alter_table_final"] = Helper.get_foreign_key_table_constraint_as_alter_table( + table_name, + foreign_table_field.table, + fname, + foreign_table_field.ref_column, + initially_deferred, + ) + final_info = "FIELD " + final_info + elif result is False: + if sql := fdata.get("sql", ""): + text["view"] = sql + ",\n" + elif foreign_table_field.field_def["type"] == "generic-relation": + text["view"] = cls.get_sql_for_relation_1_1( + table_name, + fname, + foreign_table_field.ref_column, + foreign_table_field.table, + f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}", + ) + else: + text["view"] = cls.get_sql_for_relation_1_1( + table_name, + fname, + foreign_table_field.ref_column, + foreign_table_field.table, + cast(str, foreign_table_field.column), + ) + final_info = "SQL " + final_info + else: + final_info = "NOTHING " + final_info + text["final_info"] = final_info + return text + + @classmethod + def get_sql_for_relation_1_1( + cls, + table_name: str, + fname: str, + ref_column: str, + foreign_table: str, + foreign_column: str, + ) -> str: + table_letter = Helper.get_table_letter(table_name) + letters = [table_letter] + foreign_letter = Helper.get_table_letter(foreign_table, letters) + foreign_table = Helper.get_table_name(foreign_table) + return f"(select {foreign_letter}.{ref_column} from {foreign_table} {foreign_letter} where {foreign_letter}.{foreign_column} = {table_letter}.{ref_column}) as {fname},\n" + + @classmethod + def get_relation_list_type( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + """ + ("relation-list", "relation"): False, + ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.fqid + ("relation-list", "generic-relation"): False, + ("relation-list", "generic-relation-list"): False, + ("relation-list", None): "decide_sql", // True or Exception + """ + text: SchemaZoneTexts = {} + own_table_field = TableFieldType(table_name, fname, fdata) + foreign_table_field: TableFieldType = ModelsHelper.get_definitions_from_foreign( + fdata.get("to"), + fdata.get("reference"), + ) + final_info, error = Helper.check_relation_definitions( + own_table_field, [foreign_table_field] + ) + + if not error: + if Helper.generate_field_view_or_nothing( + own_table_field, foreign_table_field + ): + if foreign_table_field.field_def.get("type") == "relation-list": + nm_table_name, value = Helper.get_nm_table_for_n_m_relation_lists(own_table_field, foreign_table_field) + if nm_table_name not in cls.intermediate_tables: + cls.intermediate_tables[nm_table_name] = value + else: + raise Exception(f"Tried to create im_table '{nm_table_name}' twice") + if sql := fdata.get("sql", ""): + text["view"] = sql + ",\n" + else: + foreign_table_column = cast(str, foreign_table_field.column) + foreign_table_field_ref_id = cast(str, foreign_table_field.ref_column) + if foreign_table_column or foreign_table_field_ref_id: + if (type_ := foreign_table_field.field_def.get("type")) == "generic-relation": + own_ref_column = own_table_field.ref_column + foreign_table_column += f"_{table_name}_{foreign_table_field.ref_column}" + foreign_table_name = foreign_table_field.table + foreign_table_ref_column = foreign_table_field.column + elif type_ == "relation-list": + if own_table_field.table == foreign_table_field.table: + """ Example: committee.forward_to_committee_ids to committee.receive_forwardings_from_committee_ids""" + own_ref_column = own_table_field.ref_column + foreign_table_ref_column = fname[:-1] + foreign_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + foreign_table_column = foreign_table_field.column[:-1] + else: + own_ref_column = own_table_field.ref_column + foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" + foreign_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + foreign_table_column = f"{own_table_field.table}_{own_table_field.ref_column}" + elif type_ == "generic-relation-list": + own_ref_column = own_table_field.ref_column + foreign_table_ref_column = foreign_table_field.column[:-1] + foreign_table_name = Helper.get_gm_table_name(foreign_table_field) + foreign_table_column = f"{foreign_table_column[:-1]}_{table_name}_id" + elif type_ == "relation" or foreign_table_field_ref_id: + own_ref_column = own_table_field.ref_column + foreign_table_ref_column = foreign_table_field.ref_column + foreign_table_name = foreign_table_field.table + foreign_table_column = foreign_table_field.column + else: + raise Exception(f"Still not implemented for foreign_table type '{type_}' in False case") + text["view"] = cls.get_sql_for_relation_n_1( + table_name, + fname, + own_ref_column, + foreign_table_name, + foreign_table_column, + foreign_table_ref_column, + ) + final_info = "SQL " + final_info + text["final_info"] = final_info + return text + + @classmethod + def get_sql_for_relation_n_1( + cls, + table_name: str, + fname: str, + own_ref_column: str, + foreign_table_name: str, + foreign_table_column: str, + foreign_table_ref_column: str, + ) -> str: + table_letter = Helper.get_table_letter(table_name) + letters = [table_letter] + foreign_letter = Helper.get_table_letter(foreign_table_name, letters) + foreign_table_name = Helper.get_table_name(foreign_table_name) + if foreign_table_column: + return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter} where {foreign_letter}.{foreign_table_column} = {table_letter}.{own_ref_column}) as {fname},\n" + else: + return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter}) as {fname},\n" + + @classmethod + def get_generic_relation_type( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts = defaultdict(str) + own_table_field = TableFieldType(table_name, fname, fdata) + foreign_table_fields: List[ + TableFieldType + ] = ModelsHelper.get_definitions_from_foreign_list( + table_name, fname, fdata.get("to"), fdata.get("reference") + ) + + error = False + final_info, error = Helper.check_relation_definitions( + own_table_field, foreign_table_fields + ) + + if not error: + if not all( + Helper.generate_field_view_or_nothing(own_table_field, foreign_table_field) + for foreign_table_field in foreign_table_fields + ): + raise Exception( + f"Error in generation for fqid '{own_table_field.fqid}'" + ) + text.update(cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"])) + initially_deferred = any( + ModelsHelper.is_fk_initially_deferred( + table_name, foreign_table_field.table + ) + for foreign_table_field in foreign_table_fields + ) + foreign_tables: List[str] = [] + for foreign_table_field in foreign_table_fields: + generic_plain_field_name = f"{own_table_field.column}_{foreign_table_field.table}_{foreign_table_field.ref_column}" + foreign_tables.append(foreign_table_field.table) + text["table"] += Helper.get_generic_combined_fields(generic_plain_field_name, own_table_field.column, foreign_table_field.table) + text["alter_table_final"] += Helper.get_foreign_key_table_constraint_as_alter_table( + own_table_field.table, + foreign_table_field.table, + generic_plain_field_name, + foreign_table_field.ref_column, + initially_deferred, + ) + text["table"] += Helper.get_generic_field_constraint(own_table_field.column, foreign_tables) + final_info = "FIELD " + final_info + text["final_info"] = final_info + return text + + @classmethod + def get_generic_relation_list_type( + cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts = {} + own_table_field = TableFieldType(table_name, fname, fdata) + foreign_table_fields: List[ + TableFieldType + ] = ModelsHelper.get_definitions_from_foreign_list( + table_name, fname, fdata.get("to"), fdata.get("reference") + ) + error = False + final_info, error = Helper.check_relation_definitions( + own_table_field, foreign_table_fields + ) + + if not error: + if not all( + Helper.generate_field_view_or_nothing(own_table_field, foreign_table_field) and foreign_table_field.field_def["type"] == "relation-list" + for foreign_table_field in foreign_table_fields + ): + raise Exception( + f"Error in generation for fqid '{own_table_field.fqid}'" + ) + # create gm-intermediate table + gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists(own_table_field, foreign_table_fields) + if gm_foreign_table not in cls.intermediate_tables: + cls.intermediate_tables[gm_foreign_table] = value + else: + raise Exception(f"Tried to create gm_table '{gm_foreign_table}' twice") + + # add field to view definition of table_name + text["view"] = cls.get_sql_for_relation_n_1( + table_name, + fname, + own_table_field.ref_column, + gm_foreign_table, + f"{own_table_field.table}_{own_table_field.ref_column}", + own_table_field.ref_column + ) + + # # add foreign key constraints for nm-relation-tables + # initially_deferred = any( + # ModelsHelper.is_fk_initially_deferred( + # table_name, foreign_table_field.table + # ) + # for foreign_table_field in foreign_table_fields + # ) + # for foreign_table_field in foreign_table_fields: + # text["alter_table_final"] = Helper.get_foreign_key_table_constraint_as_alter_table( + # table_name, + # foreign_table_field.table, + # fname[:-1], + # foreign_table_field.ref_column, + # initially_deferred, + # ) + final_info = "FIELD " + final_info + text["final_info"] = final_info + return text + + +class Helper: + ref_compiled = compiled = re.compile(r"(^\w+\b).*?\((.*?)\)") + FILE_TEMPLATE = dedent( + """ + -- schema.sql for initial database setup OpenSlides + -- Code generated. DO NOT EDIT. + + """ + ) + FIELD_TEMPLATE = string.Template( + " ${field_name} ${type}${primary_key}${required}${minimum}${minLength}${default},\n" + ) + ENUM_DEFINITION_TEMPLATE = string.Template( + dedent( + """ + DO $$$$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${enum_type}') THEN + CREATE TYPE ${enum_type} AS ENUM (${enumeration}); + ELSE + RAISE NOTICE 'type "${enum_type}" already exists, skipping'; + END IF; + END$$$$; + """ + ) + ) + INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE = string.Template( + dedent( + """ + CREATE TABLE IF NOT EXISTS ${table_name} ( + ${field1} integer NOT NULL REFERENCES ${table1} (id), + ${field2} integer NOT NULL REFERENCES ${table2} (id), + PRIMARY KEY (${list_of_keys}) + ); + """ + ) + ) + INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE = string.Template( + dedent( + """ + CREATE TABLE IF NOT EXISTS ${table_name} ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}), + ${own_table_column} varchar(100) NOT NULL, + ${foreign_table_ref_lines} + CONSTRAINT valid_${own_table_column}_part1 CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), + CONSTRAINT unique_${own_table_name_with_ref_column}_${own_table_column} UNIQUE (${own_table_name_with_ref_column}, ${own_table_column}) + ); + """ + ) + ) + GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( + " ${own_table_column}_${foreign_table_name}_id integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_table_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}T(id)," + ) + + + RELATION_LIST_AGENDA = dedent( + """ + /* Relation-list infos + Generated: What will be generated for left field + FIELD: a usual Database field + SQL: a sql-expression in a view + NOTHING: still nothing + ***: Error + Field Attributes:Field Attributes opposite side + 1: cardinality 1 + 1G: cardinality 1 with generic-relation field + n: cardinality n + nG: cardinality n with generic-relation-list field + t: "to" defined + r: "reference" defined + s: sql directive given, but must be generated + s+: sql directive includive sql-statement + R: Required + p: primary set for deciding field/sql + Model.Field -> Model.Field + model.field names + */ + + """ + ) + + @staticmethod + def get_table_name(table_name: str) -> str: + return table_name + "T" + + @staticmethod + def get_view_name(table_name: str) -> str: + if table_name in ("group", "user"): + return table_name + "_" + return table_name + + @staticmethod + def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: + """ table name n:m-relations intermediate table""" + if (own_str := f"{own.table}_{own.column}") < (foreign_str := f"{foreign.table}_{foreign.column}"): + return f"nm_{own_str}_{foreign.table}" + else: + return f"nm_{foreign_str}_{own.table}" + + @staticmethod + def get_gm_table_name(table_field: TableFieldType) -> str: + """ table name generic-list:m-relations intermediate table""" + return f"gm_{table_field.table}_{table_field.column}" + + @staticmethod + def get_table_letter(table_name: str, letters: List[str] = []) -> str: + letter = Helper.get_table_name(table_name)[0] + count = -1 + start_letter = letter + while True: + if letter in letters: + count += 1 + if count == 0: + start_letter = "".join([part[0] for part in table_name.split("_")])[ + :2 + ] + letter = start_letter + else: + letter = start_letter + str(count) + else: + return letter + + @staticmethod + def get_table_head(table_name: str) -> str: + return f"\nCREATE TABLE IF NOT EXISTS {Helper.get_table_name(table_name)} (\n" + + @staticmethod + def get_table_body_end(code: str) -> str: + code = code[:-2] + "\n" # last attribute line without ",", but with "\n" + code += ");\n\n" + return code + + @staticmethod + def get_view_head(table_name: str) -> str: + return f"\nCREATE OR REPLACE VIEW {Helper.get_view_name(table_name)} AS SELECT *,\n" + + @staticmethod + def get_view_body_end(table_name: str, code: str) -> str: + code = code[:-2] + "\n" # last attribute line without ",", but with "\n" + code += f"FROM {Helper.get_table_name(table_name)} {Helper.get_table_letter(table_name)};\n\n" + return code + + @staticmethod + def get_alter_table_final_code(code: str) -> str: + return f"-- Alter table final relation commands\n{code}\n\n" + + @staticmethod + def get_undecided_all(table_name: str, code: str) -> str: + return ( + f"/*\n Fields without SQL definition for table {table_name}\n\n{code}\n*/\n" + ) + + @staticmethod + def get_enum_type_name( + table_name: str, + fname: str, + ) -> str: + if table_name: + return f"enum_{table_name}_{fname}" + else: + return f"enum_{fname}" + + @staticmethod + def get_enum_type_definition(table_name: str, fname: str, enum_: List[Any]) -> str: + # enums per type are always strings in postgres + enumeration = ", ".join([f"'{str(item)}'" for item in enum_]) + subst = { + "enum_type": Helper.get_enum_type_name(table_name, fname), + "enumeration": enumeration, + } + return Helper.ENUM_DEFINITION_TEMPLATE.substitute(subst) + + @staticmethod + def get_foreign_key_table_constraint_as_alter_table( + table_name: str, + foreign_table: str, + own_columns: Union[List[str], str], + fk_columns: Union[List[str], str], + initially_deferred: bool = False, + delete_action: str = "", + update_action: str = "", + ) -> str: + FOREIGN_KEY_TABLE_CONSTRAINT_TEMPLATE = string.Template( + "ALTER TABLE ${own_table} ADD FOREIGN KEY(${own_columns}) REFERENCES ${foreign_table}(${fk_columns})${initially_deferred}" + ) + + if initially_deferred: + text_initially_deferred = " INITIALLY DEFERRED" + else: + text_initially_deferred = "" + if isinstance(own_columns, list): + own_columns = "(" + ", ".join(own_columns) + ")" + if isinstance(fk_columns, list): + fk_columns = "(" + ", ".join(fk_columns) + ")" + own_table = Helper.get_table_name(table_name) + foreign_table = Helper.get_table_name(foreign_table) + result = FOREIGN_KEY_TABLE_CONSTRAINT_TEMPLATE.substitute( + { + "own_table": own_table, + "foreign_table": foreign_table, + "own_columns": own_columns, + "fk_columns": fk_columns, + "initially_deferred": text_initially_deferred, + } + ) + result += Helper.get_on_action_mode(delete_action, True) + result += Helper.get_on_action_mode(update_action, False) + result += ";\n" + return result + + @staticmethod + def get_on_action_mode(action: str, delete: bool) -> str: + if action: + if (actionUpper := action.upper()) in SQL_Delete_Update_Options: + return f" ON {'DELETE' if delete else 'UPDATE'} {SQL_Delete_Update_Options(actionUpper)}" + else: + raise Exception(f"{action} is not a valid action mode") + return "" + + @staticmethod + def get_foreign_key_table_column( + to: Optional[str], reference: Optional[str] + ) -> Tuple[str, str]: + if reference: + result = Helper.ref_compiled.search(reference) + if result is None: + return reference.strip(), "id" + re_groups = result.groups() + cols = re_groups[1] + if cols: + cols = ",".join([col.strip() for col in cols.split(",")]) + else: + cols = "id" + return re_groups[0], cols + elif to: + return to.split("/")[0], "id" + else: + raise Exception("Relation field without reference or to") + + @staticmethod + def get_nm_table_for_n_m_relation_lists(own_table_field: TableFieldType, foreign_table_field: TableFieldType) -> Tuple[str, str]: + nm_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + field1 = Helper.get_field_in_n_m_relation_list(own_table_field, foreign_table_field.table) + field2 = Helper.get_field_in_n_m_relation_list(foreign_table_field, own_table_field.table) + text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute({ + "table_name": Helper.get_table_name(nm_table_name), + "field1": field1, + "table1": Helper.get_table_name(own_table_field.table), + "field2": field2, + "table2": Helper.get_table_name(foreign_table_field.table), + "list_of_keys": ", ".join([field1, field2]), + }) + return nm_table_name, text + + @staticmethod + def get_gm_table_for_gm_nm_relation_lists(own_table_field: TableFieldType, foreign_table_fields: List[TableFieldType]) -> Tuple[str, str]: + gm_table_name = Helper.get_gm_table_name(own_table_field) + joined_table_names = "('" + "', '".join([foreign_table_field.table for foreign_table_field in foreign_table_fields]) + "')" + foreign_table_ref_lines = [] + subst_dict = { + "own_table_column": own_table_field.column[:-1], + } + for foreign_table_field in foreign_table_fields: + subst_dict["foreign_table_name"] = foreign_table_field.table + foreign_table_ref_lines.append(Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict)) + + text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute({ + "table_name": Helper.get_table_name(gm_table_name), + "own_table_name": Helper.get_table_name(own_table_field.table), + "own_table_name_with_ref_column": f"{own_table_field.table}_{own_table_field.ref_column}", + "own_table_ref_column": own_table_field.ref_column, + "own_table_column": own_table_field.column[:-1], + "tuple_of_foreign_table_names": joined_table_names, + "foreign_table_ref_lines": "\n".join(foreign_table_ref_lines), + }) + return gm_table_name, text + + @staticmethod + def get_field_in_n_m_relation_list(own_table_field: TableFieldType, foreign_table_name: str) -> str: + if own_table_field.table == foreign_table_name: + return own_table_field.column[:-1] + else: + return f"{own_table_field.table}_id" + + @staticmethod + def get_initials( + table_name: str, fname: str, type_: str, fdata: Dict[str, Any] + ) -> Tuple[SubstDict, SchemaZoneTexts]: + text: SchemaZoneTexts = {} + flist: List[str] = [ + cast(str, form[1]) + for form in Formatter().parse(Helper.FIELD_TEMPLATE.template) + ] + subst: SubstDict = cast(SubstDict, {k: "" for k in flist}) + if (enum_ := fdata.get("enum")) or fdata.get("items"): + subst_type = Helper.get_enum_type_name(table_name, fname) + if not enum_: + subst_type += "[]" + else: + subst_type = FIELD_TYPES[type_]["pg_type"] + subst.update({"field_name": fname, "type": subst_type}) + if fdata.get("required"): + subst["required"] = " NOT NULL" + if (default := fdata.get("default")) is not None: + if isinstance(default, str) or type_ in ("string", "text"): + subst["default"] = f" DEFAULT '{default}'" + elif isinstance(default, (int, bool, float)): + subst["default"] = f" DEFAULT {default}" + elif isinstance(default, list): + tmp = '{"' + '", "'.join(default) + '"}' + subst["default"] = f" DEFAULT '{tmp}'" + else: + raise Exception( + f"{table_name}.{fname}: seems to be an invalid default value" + ) + if (minimum := fdata.get("minimum")) is not None: + subst[ + "minimum" + ] = f" CONSTRAINT minimum_{fname} CHECK ({fname} >= {minimum})" + if minLength := fdata.get("minLength"): + subst[ + "minLength" + ] = f" CONSTRAINT minLength_{fname} CHECK (char_length({fname}) >= {minLength})" + if comment := fdata.get("description"): + text[ + "alter_table" + ] = f"comment on column {Helper.get_table_name(table_name)}.{fname} is '{comment}';\n" + return subst, text + + @staticmethod + def get_cardinality(field: Optional[Dict[str, Any]]) -> Tuple[str, bool]: + """ + Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required, p=primary + """ + if field: + required = bool(field.get("required")) + sql = "sql" in field + sql_empty = field.get("sql") == "" + to = bool(field.get("to")) + reference = bool(field.get("reference")) + primary = bool(field.get("primary")) + + # general rules of inconsistent field descriptions on field level + error = ( + (required and sql) + or (required and not (to or reference)) + or (not required and sql_empty and not to) + or not (required or sql or to or reference) + ) + if field["type"] == "relation": + result = "1" + elif field["type"] == "relation-list": + if field.get("required"): + error = True + result = "n" + elif field["type"] == "generic-relation": + result = "1G" + elif field["type"] == "generic-relation-list": + if field.get("required"): + error = True + result = "nG" + else: + raise Exception( + f"Not implemented type {field['type']} in method get_cardinality found!" + ) + if reference: + result += "r" + if to: + result += "t" + if sql: + result += "s" + if field["sql"]: + result += "+" + else: + result += "-" + if required: + result += "R" + if primary: + result += "p" + else: + result = "" + error = False + return result, error + + @staticmethod + def check_relation_definitions( + own: TableFieldType, foreigns: List[TableFieldType] + ) -> Tuple[str, bool]: + error = False + text = "" + own_c, tmp_error = Helper.get_cardinality(own.field_def) + error = error or tmp_error + foreigns_c = [] + foreign_fqids = [] + for foreign in foreigns: + foreign_c, tmp_error = Helper.get_cardinality(foreign.field_def) + foreigns_c.append(foreign_c) + error = error or tmp_error + foreign_fqids.append(foreign.fqid) + + # if table_field["type"] == "relation": + # if foreign_field and foreign_field.get("type") == "relation": + # if (("sql" in table_field) == ("sql" in foreign_field)) and ( + # ("required" in table_field) == ("required" in foreign_field) + # ): + # error = True + # elif table_field["type"] == "relation-list": + # if foreign_field and foreign_field.get("type") == "relation": + # if ( + # not (table_field.get("sql")) or (foreign_field.get("to")) + # ) or table_field["required"]: + # error = True + if error: + text = "*** " + text += f"{own_c}:{','.join(foreigns_c)} => {own.fqid}:-> {','.join(foreign_fqids)}\n" + return text, error + + @staticmethod + def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) -> bool: + """Decides, whether a relation field will be physical, view field or nothing + Returns True = if necessary build table, view or intermediate Tables etc. + False = do only extra + """ + decision_list = { + ("relation", "relation"): "decide_primary_side", + ("relation", "relation-list"): True, + ("relation", "generic-relation"): False, + ("relation", "generic-relation-list"): "not implemented", + ("relation", None): True, + ("relation-list", "relation"): False, + ("relation-list", "relation-list"): "decide_alphabetical", + ("relation-list", "generic-relation"): False, + ("relation-list", "generic-relation-list"): False, + ("relation-list", None): "decide_sql", + ("generic-relation", "relation"): True, + ("generic-relation", "relation-list"): True, + ("generic-relation", "generic-relation"): "not implemented", + ("generic-relation", "generic-relation-list"): "not implemented", + ("generic-relation", None): True, + ("generic-relation-list", "relation"): "not implemented", + ("generic-relation-list", "relation-list"): True, + ("generic-relation-list", "generic-relation"): "not implemented", + ("generic-relation-list", "generic-relation-list"): "not implemented", + ("generic-relation-list", None): "not implemented", + } + result = decision_list[ + ( + own_type := own.field_def.get("type", ""), + foreign_type := ( + foreign.field_def.get("type") if foreign.field_def else None + ), + ) + ] + if result == "not implemented": + raise Exception( + f"Type combination not implemented: {own_type}:{foreign_type} on field {own.fqid}" + ) + elif result == "decide_primary_side": + if own.field_def.get("required", False) == foreign.field_def.get( + "required", False + ): + if bool(own.field_def.get("sql", False)) == bool( + foreign.field_def.get("sql", False) + ): + if bool(own.field_def.get("reference", False)) == bool( + foreign.field_def.get("reference", False) + ): + if own.field_def.get("primary", False) == foreign.field_def.get("primary", False): + raise Exception( + f"Type combination undecidable: {own_type}:{foreign_type} on field {own.fqid}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" + ) + else: + return own.field_def.get("primary", False) + else: + return bool(own.field_def.get("reference", False)) + else: + return bool(foreign.field_def.get("sql", False)) + else: + return own.field_def.get("required", False) + elif result == "decide_alphabetical": + return foreign.fqid == "-" or own.fqid < foreign.fqid + elif result == "decide_sql": + if own.field_def.get("sql") or own.field_def.get("reference"): + return True + else: + raise Exception(f"Missing sql-or to-attribute for field {own.fqid}") + return cast(bool, result) + + @staticmethod + def get_generic_combined_fields(generic_plain_field_name: str, own_column: str, foreign_table:str) -> str: + return f" {generic_plain_field_name} integer GENERATED ALWAYS AS (CASE WHEN split_part({own_column}, '/', 1) = '{foreign_table}' THEN cast(split_part({own_column}, '/', 2) AS INTEGER) ELSE null END) STORED,\n" + + @staticmethod + def get_generic_field_constraint(own_column:str, foreign_tables:List[str]) -> str: + return f""" CONSTRAINT valid_{own_column}_part1 CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" + + +class ModelsHelper: + @staticmethod + def is_fk_initially_deferred(own_table: str, foreign_table: str) -> bool: + """ + The "Initially deferred" in fk-definition is necessary, + if 2 related tables require both the relation to the other table + """ + + def _first_to_second(t1: str, t2: str) -> bool: + for field in MODELS[t1].values(): + if field.get("required") and field["type"].startswith("relation"): + ftable, _ = Helper.get_foreign_key_table_column( + field.get("to"), field.get("reference") + ) + if ftable == t2: + return True + return False + + if _first_to_second(own_table, foreign_table): + return _first_to_second(foreign_table, own_table) + return False + + @staticmethod + def get_definitions_from_foreign( + to: Optional[str], reference: Optional[str] + ) -> TableFieldType: + tname = "" + fname = "" + tfield: Dict[str, Any] = {} + ref_column = "" + if to: + tname, fname, tfield = ModelsHelper.get_field_definition_from_to(to) + ref_column = "id" + if reference: + tname, ref_column = Helper.get_foreign_key_table_column(to, reference) + # if not fname: + # fname = ref_column + # tfield = {"type": "relation", "reference": reference} + return TableFieldType(tname, fname, tfield, ref_column) + + @staticmethod + def get_definitions_from_foreign_list( + table: str, + field: str, + to: Optional[Union[ToDict, List[str]]], + reference: Optional[List[str]], + ) -> List[TableFieldType]: + """ + used for generic_relation with multiple foreign relations + """ + if to and reference: + raise Exception( + f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" + ) + results: List[TableFieldType] = [] + if isinstance(to, dict): + fname = "/" + to["field"] + for table in to["collections"]: + results.append( + ModelsHelper.get_definitions_from_foreign(table + fname, None) + ) + elif isinstance(to, list): + for fqid in to: + results.append(ModelsHelper.get_definitions_from_foreign(fqid, None)) + elif reference: + for ref in reference: + results.append(ModelsHelper.get_definitions_from_foreign(None, ref)) + return results + + @staticmethod + def get_field_definition_from_to(to: str) -> Tuple[str, str, Dict[str, Any]]: + tname, fname = to.split("/") + try: + field = MODELS[tname][fname] + except Exception as e: + field = {} + return tname, fname, field + + +FIELD_TYPES: Dict[str, Dict[str, Any]] = { + "string": { + "pg_type": string.Template("varchar(${maxLength})"), + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "number": { + "pg_type": "integer", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "boolean": { + "pg_type": "boolean", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "JSON": {"pg_type": "jsonb", "method": GenerateCodeBlocks.get_schema_simple_types}, + "HTMLStrict": { + "pg_type": "text", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "HTMLPermissive": { + "pg_type": "text", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "float": {"pg_type": "real", "method": GenerateCodeBlocks.get_schema_simple_types}, + "decimal": { + "pg_type": string.Template("decimal(${maxLength})"), + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "decimal(6)": { + "pg_type": "decimal(6)", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "timestamp": { + "pg_type": "timestamptz", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "color": { + "pg_type": string.Template( + "integer CHECK (${field_name} >= 0 and ${field_name} <= 16777215)" + ), + "method": GenerateCodeBlocks.get_schema_color, + }, + "string[]": { + "pg_type": string.Template("varchar(${maxLength})[]"), + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "number[]": { + "pg_type": "integer[]", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, + "text": {"pg_type": "text", "method": GenerateCodeBlocks.get_schema_simple_types}, + "relation": {"pg_type": "integer", "method": GenerateCodeBlocks.get_relation_type}, + "relation-list": { + "pg_type": "integer[]", + "method": GenerateCodeBlocks.get_relation_list_type, + }, + "generic-relation": { + "pg_type": "varchar(100)", + "method": GenerateCodeBlocks.get_generic_relation_type, + }, + "generic-relation-list": { + "pg_type": "varchar(100)[]", + "method": GenerateCodeBlocks.get_generic_relation_list_type, + }, + # special defined + "primary_key": { + "pg_type": "integer", + "method": GenerateCodeBlocks.get_schema_primary_key, + }, +} + + +def main() -> None: + """ + Main entry point for this script to generate the schema.sql from models.yml. + """ + + global MODELS + + # Retrieve models.yml from call-parameter for testing purposes, local file or GitHub + if len(sys.argv) > 1: + file = sys.argv[1] + else: + file = SOURCE + + if os.path.isfile(file): + with open(file, "rb") as x: + models_yml = x.read() + else: + models_yml = requests.get(file).content + + # calc checksum to assert the schema.sql is up-to-date + checksum = hashlib.md5(models_yml).hexdigest() + + if len(sys.argv) > 1 and sys.argv[1] == "check": + from openslides_backend.models.models import MODELS_YML_CHECKSUM + + assert checksum == MODELS_YML_CHECKSUM + print("models.py is up to date (checksum-comparison)") + sys.exit(0) + + # Fix broken keys + models_yml = models_yml.replace(" yes:".encode(), ' "yes":'.encode()) + models_yml = models_yml.replace(" no:".encode(), ' "no":'.encode()) + + # Load and parse models.yml + MODELS = yaml.safe_load(models_yml) + + ( + pre_code, + table_name_code, + view_name_code, + alter_table_code, + final_info_code, + missing_handled_attributes, + im_table_code + ) = GenerateCodeBlocks.generate_the_code() + with open(DESTINATION, "w") as dest: + dest.write(Helper.FILE_TEMPLATE) + dest.write("-- MODELS_YML_CHECKSUM = " + repr(checksum) + "\n") + dest.write("-- Type definitions") + dest.write(pre_code) + dest.write("\n\n-- Table definitions") + dest.write(table_name_code) + dest.write("\n\n-- Intermediate table definitions\n") + dest.write(im_table_code) + dest.write("-- View definitions\n") + dest.write(view_name_code) + dest.write("-- Alter table relations\n") + dest.write(alter_table_code) + dest.write(Helper.RELATION_LIST_AGENDA) + dest.write("/*\n") + dest.write(final_info_code) + dest.write("*/\n") + dest.write( + f"\n/* Missing attribute handling for {', '.join(missing_handled_attributes)} */" + ) + print(f"Models file {DESTINATION} successfully created.") + + +if __name__ == "__main__": + main() diff --git a/models_validator/requirements.txt b/cli/requirements.txt similarity index 100% rename from models_validator/requirements.txt rename to cli/requirements.txt diff --git a/models_validator/validate.py b/cli/validate.py similarity index 99% rename from models_validator/validate.py rename to cli/validate.py index 25858f67..2f7ea3fc 100644 --- a/models_validator/validate.py +++ b/cli/validate.py @@ -18,7 +18,7 @@ COLOR_REGEX = re.compile(r"^#[0-9a-f]{6}$") DEFAULT_FILES = [ - "../models.yml", + "global/meta/models.yml", ] RELATION_TYPES = ( diff --git a/models.yml b/models.yml index 438581fc..fe903509 100644 --- a/models.yml +++ b/models.yml @@ -17,7 +17,9 @@ # and `generic-relation-list`. # - Non-generic relations: The simple syntax for such a field # `to: /`. This is a reference to a collection. The reverse -# relation field in this collection is . E. g. in a motion the field +# relation field in this collection is . E. g. in a motion the field `meeting_id` +# `reference: , where the collection is the same as that from the `to`. # `category_id` links to one category where the field `motion_ids` contains the # motion id. The simple notation for the field is `motion_category/motion_ids`. # The reverse field has type `relation-list` and is related back to @@ -35,7 +37,8 @@ # Or `to` can be a list of collection fields: # to: # - motion/option_ids -# - user/option_$_ids +# - user/option_ids +# - poll_candidate_list/option_id # - on_delete: This fields determines what should happen with the foreign model if # this model gets deleted. Possible values are: # - SET_NULL (default): delete the id from the foreign key @@ -173,7 +176,7 @@ organization: committee_ids: type: relation-list restriction_mode: B - to: committee/organization_id + sql: (select array_agg(c.id) from committeeT c) as committee_ids active_meeting_ids: type: relation-list restriction_mode: B @@ -189,16 +192,16 @@ organization: organization_tag_ids: type: relation-list restriction_mode: B - to: organization_tag/organization_id + reference: organization_tag(id) theme_id: type: relation required: true restriction_mode: A - to: theme/theme_for_organization_id + reference: theme(id) theme_ids: type: relation-list restriction_mode: A - to: theme/organization_id + reference: theme(id) mediafile_ids: type: relation-list to: mediafile/owner_id @@ -207,7 +210,7 @@ organization: user_ids: type: relation-list restriction_mode: C - to: user/organization_id + reference: user(id) users_email_sender: type: string default: OpenSlides @@ -332,12 +335,22 @@ user: # Calculates all committee's where the user is # - committee-manager (= committees in committee_management_ids) # - is member (= has group-rights) of a meeting in the committee + # TODO: fix sql, not correct for the current design and naming committee_ids: - type: relation-list + type: number[] to: committee/user_ids restriction_mode: E read_only: true - description: "Calculated field." + description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" + sql: >- + select array_agg(committee_id) from ( + select m.committee_id as committee_id from group_to_user gtu + join groupT g on g.id = gtu.group_id + join meetingT m on m.id = g.meeting_id + where gtu.user_id = u.id + union + select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id + ) as cs # committee specific permissions committee_management_ids: @@ -382,12 +395,6 @@ user: description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true restriction_mode: E - organization_id: - type: relation - to: organization/user_ids - required: True - restriction_mode: F - constant: true meeting_user: id: @@ -506,11 +513,6 @@ organization_tag: - meeting field: organization_tag_ids restriction_mode: A - organization_id: - type: relation - to: organization/organization_tag_ids - restriction_mode: A - required: true theme: id: @@ -667,12 +669,6 @@ theme: restriction_mode: A to: organization/theme_id type: relation - organization_id: - required: true - restriction_mode: A - to: organization/theme_ids - type: relation - constant: true committee: id: @@ -699,12 +695,22 @@ committee: type: relation to: meeting/default_meeting_for_committee_id restriction_mode: A + primary: True user_ids: - type: relation-list + type: number[] to: user/committee_ids restriction_mode: A read_only: true - description: "Calculated field." + description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" + sql: >- + select array_agg(user_id) from ( + select gtu.user_id as user_id from meetingT m + join groupT g on g.meeting_id = m.id + join group_to_user gtu on gtu.group_id = g.id + where m.committee_id = c.id + union + select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id + ) as x manager_ids: type: relation-list to: user/committee_management_ids @@ -726,12 +732,6 @@ committee: type: relation-list to: organization_tag/tagged_ids restriction_mode: A - organization_id: - type: relation - to: organization/committee_ids - required: true - restriction_mode: A - constant: true meeting: id: @@ -919,7 +919,7 @@ meeting: default: center restriction_mode: B export_pdf_fontsize: - type: number + type: string enum: - 10 - 11 @@ -1592,66 +1592,82 @@ meeting: type: relation to: mediafile/used_as_logo_projector_main_in_meeting_id restriction_mode: B + primary: True logo_projector_header_id: type: relation to: mediafile/used_as_logo_projector_header_in_meeting_id restriction_mode: B + primary: True logo_web_header_id: type: relation to: mediafile/used_as_logo_web_header_in_meeting_id restriction_mode: B + primary: True logo_pdf_header_l_id: type: relation to: mediafile/used_as_logo_pdf_header_l_in_meeting_id restriction_mode: B + primary: True logo_pdf_header_r_id: type: relation to: mediafile/used_as_logo_pdf_header_r_in_meeting_id restriction_mode: B + primary: True logo_pdf_footer_l_id: type: relation to: mediafile/used_as_logo_pdf_footer_l_in_meeting_id restriction_mode: B + primary: True logo_pdf_footer_r_id: type: relation to: mediafile/used_as_logo_pdf_footer_r_in_meeting_id restriction_mode: B + primary: True logo_pdf_ballot_paper_id: type: relation to: mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id restriction_mode: B + primary: True font_regular_id: type: relation to: mediafile/used_as_font_regular_in_meeting_id restriction_mode: B + primary: True font_italic_id: type: relation to: mediafile/used_as_font_italic_in_meeting_id restriction_mode: B + primary: True font_bold_id: type: relation to: mediafile/used_as_font_bold_in_meeting_id restriction_mode: B + primary: True font_bold_italic_id: type: relation to: mediafile/used_as_font_bold_italic_in_meeting_id restriction_mode: B + primary: True font_monospace_id: type: relation to: mediafile/used_as_font_monospace_in_meeting_id restriction_mode: B + primary: True font_chyron_speaker_name_id: type: relation to: mediafile/used_as_font_chyron_speaker_name_in_meeting_id restriction_mode: B + primary: True font_projector_h1_id: type: relation to: mediafile/used_as_font_projector_h1_in_meeting_id restriction_mode: B + primary: True font_projector_h2_id: type: relation to: mediafile/used_as_font_projector_h2_in_meeting_id restriction_mode: B + primary: True # Other relations committee_id: type: relation @@ -1683,11 +1699,11 @@ meeting: restriction_mode: B list_of_speakers_countdown_id: type: relation - to: projector_countdown/used_as_list_of_speakers_countdown_meeting_id + reference: projector_countdown(id) restriction_mode: B poll_countdown_id: type: relation - to: projector_countdown/used_as_poll_countdown_meeting_id + reference: projector_countdown(id) restriction_mode: B projection_ids: type: relation-list @@ -1773,6 +1789,7 @@ meeting: type: relation to: group/admin_group_for_meeting_id restriction_mode: B + primary: True group: id: @@ -3128,7 +3145,7 @@ poll: restriction_mode: A global_option_id: type: relation - to: option/used_as_global_option_in_poll_id + reference: option(id) on_delete: CASCADE equal_fields: meeting_id restriction_mode: A diff --git a/schema_relational.sql b/schema_relational.sql new file mode 100644 index 00000000..11b1ea5d --- /dev/null +++ b/schema_relational.sql @@ -0,0 +1,2350 @@ + +-- schema.sql for initial database setup OpenSlides +-- Code generated. DO NOT EDIT. + +-- MODELS_YML_CHECKSUM = 'faa2401887834e2fdfbb875185412256' +-- Type definitions +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_organization_default_language') THEN + CREATE TYPE enum_organization_default_language AS ENUM ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr'); + ELSE + RAISE NOTICE 'type "enum_organization_default_language" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_user_organization_management_level') THEN + CREATE TYPE enum_user_organization_management_level AS ENUM ('superadmin', 'can_manage_organization', 'can_manage_users'); + ELSE + RAISE NOTICE 'type "enum_user_organization_management_level" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_language') THEN + CREATE TYPE enum_meeting_language AS ENUM ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr'); + ELSE + RAISE NOTICE 'type "enum_meeting_language" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_applause_type') THEN + CREATE TYPE enum_meeting_applause_type AS ENUM ('applause-type-bar', 'applause-type-particles'); + ELSE + RAISE NOTICE 'type "enum_meeting_applause_type" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_csv_encoding') THEN + CREATE TYPE enum_meeting_export_csv_encoding AS ENUM ('utf-8', 'iso-8859-15'); + ELSE + RAISE NOTICE 'type "enum_meeting_export_csv_encoding" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_pagenumber_alignment') THEN + CREATE TYPE enum_meeting_export_pdf_pagenumber_alignment AS ENUM ('left', 'right', 'center'); + ELSE + RAISE NOTICE 'type "enum_meeting_export_pdf_pagenumber_alignment" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_fontsize') THEN + CREATE TYPE enum_meeting_export_pdf_fontsize AS ENUM ('10', '11', '12'); + ELSE + RAISE NOTICE 'type "enum_meeting_export_pdf_fontsize" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_pagesize') THEN + CREATE TYPE enum_meeting_export_pdf_pagesize AS ENUM ('A4', 'A5'); + ELSE + RAISE NOTICE 'type "enum_meeting_export_pdf_pagesize" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_numeral_system') THEN + CREATE TYPE enum_meeting_agenda_numeral_system AS ENUM ('arabic', 'roman'); + ELSE + RAISE NOTICE 'type "enum_meeting_agenda_numeral_system" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_item_creation') THEN + CREATE TYPE enum_meeting_agenda_item_creation AS ENUM ('always', 'never', 'default_yes', 'default_no'); + ELSE + RAISE NOTICE 'type "enum_meeting_agenda_item_creation" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_new_items_default_visibility') THEN + CREATE TYPE enum_meeting_agenda_new_items_default_visibility AS ENUM ('common', 'internal', 'hidden'); + ELSE + RAISE NOTICE 'type "enum_meeting_agenda_new_items_default_visibility" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_default_line_numbering') THEN + CREATE TYPE enum_meeting_motions_default_line_numbering AS ENUM ('outside', 'inline', 'none'); + ELSE + RAISE NOTICE 'type "enum_meeting_motions_default_line_numbering" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_recommendation_text_mode') THEN + CREATE TYPE enum_meeting_motions_recommendation_text_mode AS ENUM ('original', 'changed', 'diff', 'agreed'); + ELSE + RAISE NOTICE 'type "enum_meeting_motions_recommendation_text_mode" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_default_sorting') THEN + CREATE TYPE enum_meeting_motions_default_sorting AS ENUM ('number', 'weight'); + ELSE + RAISE NOTICE 'type "enum_meeting_motions_default_sorting" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_number_type') THEN + CREATE TYPE enum_meeting_motions_number_type AS ENUM ('per_category', 'serially_numbered', 'manually'); + ELSE + RAISE NOTICE 'type "enum_meeting_motions_number_type" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_amendments_text_mode') THEN + CREATE TYPE enum_meeting_motions_amendments_text_mode AS ENUM ('freestyle', 'fulltext', 'paragraph'); + ELSE + RAISE NOTICE 'type "enum_meeting_motions_amendments_text_mode" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_ballot_paper_selection') THEN + CREATE TYPE enum_meeting_motion_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); + ELSE + RAISE NOTICE 'type "enum_meeting_motion_poll_ballot_paper_selection" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_default_onehundred_percent_base') THEN + CREATE TYPE enum_meeting_motion_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); + ELSE + RAISE NOTICE 'type "enum_meeting_motion_poll_default_onehundred_percent_base" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_default_backend') THEN + CREATE TYPE enum_meeting_motion_poll_default_backend AS ENUM ('long', 'fast'); + ELSE + RAISE NOTICE 'type "enum_meeting_motion_poll_default_backend" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_users_pdf_wlan_encryption') THEN + CREATE TYPE enum_meeting_users_pdf_wlan_encryption AS ENUM ('', 'WEP', 'WPA', 'nopass'); + ELSE + RAISE NOTICE 'type "enum_meeting_users_pdf_wlan_encryption" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_ballot_paper_selection') THEN + CREATE TYPE enum_meeting_assignment_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); + ELSE + RAISE NOTICE 'type "enum_meeting_assignment_poll_ballot_paper_selection" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_default_onehundred_percent_base') THEN + CREATE TYPE enum_meeting_assignment_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); + ELSE + RAISE NOTICE 'type "enum_meeting_assignment_poll_default_onehundred_percent_base" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_default_backend') THEN + CREATE TYPE enum_meeting_assignment_poll_default_backend AS ENUM ('long', 'fast'); + ELSE + RAISE NOTICE 'type "enum_meeting_assignment_poll_default_backend" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_ballot_paper_selection') THEN + CREATE TYPE enum_meeting_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); + ELSE + RAISE NOTICE 'type "enum_meeting_poll_ballot_paper_selection" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_default_onehundred_percent_base') THEN + CREATE TYPE enum_meeting_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); + ELSE + RAISE NOTICE 'type "enum_meeting_poll_default_onehundred_percent_base" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_default_backend') THEN + CREATE TYPE enum_meeting_poll_default_backend AS ENUM ('long', 'fast'); + ELSE + RAISE NOTICE 'type "enum_meeting_poll_default_backend" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_group_permissions') THEN + CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see'); + ELSE + RAISE NOTICE 'type "enum_group_permissions" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_agenda_item_type') THEN + CREATE TYPE enum_agenda_item_type AS ENUM ('common', 'internal', 'hidden'); + ELSE + RAISE NOTICE 'type "enum_agenda_item_type" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_speaker_speech_state') THEN + CREATE TYPE enum_speaker_speech_state AS ENUM ('contribution', 'pro', 'contra'); + ELSE + RAISE NOTICE 'type "enum_speaker_speech_state" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_change_recommendation_type') THEN + CREATE TYPE enum_motion_change_recommendation_type AS ENUM ('replacement', 'insertion', 'deletion', 'other'); + ELSE + RAISE NOTICE 'type "enum_motion_change_recommendation_type" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_css_class') THEN + CREATE TYPE enum_motion_state_css_class AS ENUM ('grey', 'red', 'green', 'lightblue', 'yellow'); + ELSE + RAISE NOTICE 'type "enum_motion_state_css_class" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_restrictions') THEN + CREATE TYPE enum_motion_state_restrictions AS ENUM ('motion.can_see_internal', 'motion.can_manage_metadata', 'motion.can_manage', 'is_submitter'); + ELSE + RAISE NOTICE 'type "enum_motion_state_restrictions" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_merge_amendment_into_final') THEN + CREATE TYPE enum_motion_state_merge_amendment_into_final AS ENUM ('do_not_merge', 'undefined', 'do_merge'); + ELSE + RAISE NOTICE 'type "enum_motion_state_merge_amendment_into_final" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_type') THEN + CREATE TYPE enum_poll_type AS ENUM ('analog', 'named', 'pseudoanonymous', 'cryptographic'); + ELSE + RAISE NOTICE 'type "enum_poll_type" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_backend') THEN + CREATE TYPE enum_poll_backend AS ENUM ('long', 'fast'); + ELSE + RAISE NOTICE 'type "enum_poll_backend" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_pollmethod') THEN + CREATE TYPE enum_poll_pollmethod AS ENUM ('Y', 'YN', 'YNA', 'N'); + ELSE + RAISE NOTICE 'type "enum_poll_pollmethod" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_state') THEN + CREATE TYPE enum_poll_state AS ENUM ('created', 'started', 'finished', 'published'); + ELSE + RAISE NOTICE 'type "enum_poll_state" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_onehundred_percent_base') THEN + CREATE TYPE enum_poll_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); + ELSE + RAISE NOTICE 'type "enum_poll_onehundred_percent_base" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_assignment_phase') THEN + CREATE TYPE enum_assignment_phase AS ENUM ('search', 'voting', 'finished'); + ELSE + RAISE NOTICE 'type "enum_assignment_phase" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_action_worker_state') THEN + CREATE TYPE enum_action_worker_state AS ENUM ('running', 'end', 'aborted'); + ELSE + RAISE NOTICE 'type "enum_action_worker_state" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_import_preview_name') THEN + CREATE TYPE enum_import_preview_name AS ENUM ('account', 'participant', 'topic', 'committee'); + ELSE + RAISE NOTICE 'type "enum_import_preview_name" already exists, skipping'; + END IF; +END$$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_import_preview_state') THEN + CREATE TYPE enum_import_preview_state AS ENUM ('warning', 'error', 'done'); + ELSE + RAISE NOTICE 'type "enum_import_preview_state" already exists, skipping'; + END IF; +END$$; + + +-- Table definitions +CREATE TABLE IF NOT EXISTS organizationT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256), + description text, + legal_notice text, + privacy_policy text, + login_text text, + reset_password_verbose_errors boolean, + genders varchar(256)[] DEFAULT '{"male", "female", "diverse", "non-binary"}', + enable_electronic_voting boolean, + enable_chat boolean, + limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, + limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, + default_language enum_organization_default_language NOT NULL, + saml_enabled boolean, + saml_login_button_text varchar(256) DEFAULT 'SAML login', + saml_attr_mapping jsonb, + saml_metadata_idp text, + saml_metadata_sp text, + saml_private_key text, + theme_id integer NOT NULL, + users_email_sender varchar(256) DEFAULT 'OpenSlides', + users_email_replyto varchar(256), + users_email_subject varchar(256) DEFAULT 'OpenSlides access data', + users_email_body text DEFAULT 'Dear {name}, + +this is your personal OpenSlides login: + +{url} +Username: {username} +Password: {password} + + +This email was generated automatically.', + url varchar(256) DEFAULT 'https://example.com' +); + + + +comment on column organizationT.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; +comment on column organizationT.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; + +/* + Fields without SQL definition for table organization + + vote_decrypt_public_main_key type:string is marked as a calculated field + +*/ + +CREATE TABLE IF NOT EXISTS userT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + username varchar(256) NOT NULL, + saml_id varchar(256) CONSTRAINT minLength_saml_id CHECK (char_length(saml_id) >= 1), + pronoun varchar(32), + title varchar(256), + first_name varchar(256), + last_name varchar(256), + is_active boolean, + is_physical_person boolean DEFAULT True, + password varchar(256), + default_password varchar(256), + can_change_own_password boolean DEFAULT True, + gender varchar(256), + email varchar(256), + default_number varchar(256), + default_structure_level varchar(256), + default_vote_weight decimal(6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', + last_email_sent timestamptz, + is_demo_user boolean, + last_login timestamptz, + organization_management_level enum_user_organization_management_level, + committee_ids integer[], + meeting_ids integer[] +); + + + +comment on column userT.saml_id is 'unique-key from IdP for SAML login'; +comment on column userT.organization_management_level is 'Hierarchical permission level for the whole organization.'; +comment on column userT.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column userT.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; + + +CREATE TABLE IF NOT EXISTS meeting_userT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + comment text, + number varchar(256), + structure_level varchar(256), + about_me text, + vote_weight decimal(6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), + user_id integer NOT NULL, + meeting_id integer NOT NULL, + vote_delegated_to_id integer +); + + + + +CREATE TABLE IF NOT EXISTS organization_tagT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + color integer CHECK (color >= 0 and color <= 16777215) NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS themeT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name varchar(256) NOT NULL, + accent_100 integer CHECK (accent_100 >= 0 and accent_100 <= 16777215), + accent_200 integer CHECK (accent_200 >= 0 and accent_200 <= 16777215), + accent_300 integer CHECK (accent_300 >= 0 and accent_300 <= 16777215), + accent_400 integer CHECK (accent_400 >= 0 and accent_400 <= 16777215), + accent_50 integer CHECK (accent_50 >= 0 and accent_50 <= 16777215), + accent_500 integer CHECK (accent_500 >= 0 and accent_500 <= 16777215) NOT NULL, + accent_600 integer CHECK (accent_600 >= 0 and accent_600 <= 16777215), + accent_700 integer CHECK (accent_700 >= 0 and accent_700 <= 16777215), + accent_800 integer CHECK (accent_800 >= 0 and accent_800 <= 16777215), + accent_900 integer CHECK (accent_900 >= 0 and accent_900 <= 16777215), + accent_a100 integer CHECK (accent_a100 >= 0 and accent_a100 <= 16777215), + accent_a200 integer CHECK (accent_a200 >= 0 and accent_a200 <= 16777215), + accent_a400 integer CHECK (accent_a400 >= 0 and accent_a400 <= 16777215), + accent_a700 integer CHECK (accent_a700 >= 0 and accent_a700 <= 16777215), + primary_100 integer CHECK (primary_100 >= 0 and primary_100 <= 16777215), + primary_200 integer CHECK (primary_200 >= 0 and primary_200 <= 16777215), + primary_300 integer CHECK (primary_300 >= 0 and primary_300 <= 16777215), + primary_400 integer CHECK (primary_400 >= 0 and primary_400 <= 16777215), + primary_50 integer CHECK (primary_50 >= 0 and primary_50 <= 16777215), + primary_500 integer CHECK (primary_500 >= 0 and primary_500 <= 16777215) NOT NULL, + primary_600 integer CHECK (primary_600 >= 0 and primary_600 <= 16777215), + primary_700 integer CHECK (primary_700 >= 0 and primary_700 <= 16777215), + primary_800 integer CHECK (primary_800 >= 0 and primary_800 <= 16777215), + primary_900 integer CHECK (primary_900 >= 0 and primary_900 <= 16777215), + primary_a100 integer CHECK (primary_a100 >= 0 and primary_a100 <= 16777215), + primary_a200 integer CHECK (primary_a200 >= 0 and primary_a200 <= 16777215), + primary_a400 integer CHECK (primary_a400 >= 0 and primary_a400 <= 16777215), + primary_a700 integer CHECK (primary_a700 >= 0 and primary_a700 <= 16777215), + warn_100 integer CHECK (warn_100 >= 0 and warn_100 <= 16777215), + warn_200 integer CHECK (warn_200 >= 0 and warn_200 <= 16777215), + warn_300 integer CHECK (warn_300 >= 0 and warn_300 <= 16777215), + warn_400 integer CHECK (warn_400 >= 0 and warn_400 <= 16777215), + warn_50 integer CHECK (warn_50 >= 0 and warn_50 <= 16777215), + warn_500 integer CHECK (warn_500 >= 0 and warn_500 <= 16777215) NOT NULL, + warn_600 integer CHECK (warn_600 >= 0 and warn_600 <= 16777215), + warn_700 integer CHECK (warn_700 >= 0 and warn_700 <= 16777215), + warn_800 integer CHECK (warn_800 >= 0 and warn_800 <= 16777215), + warn_900 integer CHECK (warn_900 >= 0 and warn_900 <= 16777215), + warn_a100 integer CHECK (warn_a100 >= 0 and warn_a100 <= 16777215), + warn_a200 integer CHECK (warn_a200 >= 0 and warn_a200 <= 16777215), + warn_a400 integer CHECK (warn_a400 >= 0 and warn_a400 <= 16777215), + warn_a700 integer CHECK (warn_a700 >= 0 and warn_a700 <= 16777215), + headbar integer CHECK (headbar >= 0 and headbar <= 16777215), + yes integer CHECK (yes >= 0 and yes <= 16777215), + no integer CHECK (no >= 0 and no <= 16777215), + abstain integer CHECK (abstain >= 0 and abstain <= 16777215) +); + + + + +CREATE TABLE IF NOT EXISTS committeeT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + description text, + external_id varchar(256), + default_meeting_id integer, + user_ids integer[], + forwarding_user_id integer +); + + + +comment on column committeeT.external_id is 'unique'; +comment on column committeeT.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; + + +CREATE TABLE IF NOT EXISTS meetingT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + external_id varchar(256), + welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', + welcome_text text DEFAULT 'Space for your welcome text.', + name varchar(100) NOT NULL DEFAULT 'OpenSlides', + is_active_in_organization_id integer, + is_archived_in_organization_id integer, + description varchar(100) DEFAULT 'Presentation and assembly system', + location varchar(256), + start_time timestamptz, + end_time timestamptz, + imported_at timestamptz, + language enum_meeting_language NOT NULL, + jitsi_domain varchar(256), + jitsi_room_name varchar(256), + jitsi_room_password varchar(256), + template_for_organization_id integer, + enable_anonymous boolean DEFAULT False, + custom_translations jsonb, + conference_show boolean DEFAULT False, + conference_auto_connect boolean DEFAULT False, + conference_los_restriction boolean DEFAULT True, + conference_stream_url varchar(256), + conference_stream_poster_url varchar(256), + conference_open_microphone boolean DEFAULT False, + conference_open_video boolean DEFAULT False, + conference_auto_connect_next_speakers integer DEFAULT 0, + conference_enable_helpdesk boolean DEFAULT False, + applause_enable boolean DEFAULT False, + applause_type enum_meeting_applause_type DEFAULT 'applause-type-bar', + applause_show_level boolean DEFAULT False, + applause_min_amount integer CONSTRAINT minimum_applause_min_amount CHECK (applause_min_amount >= 0) DEFAULT 1, + applause_max_amount integer CONSTRAINT minimum_applause_max_amount CHECK (applause_max_amount >= 0) DEFAULT 0, + applause_timeout integer CONSTRAINT minimum_applause_timeout CHECK (applause_timeout >= 0) DEFAULT 5, + applause_particle_image_url varchar(256), + projector_countdown_default_time integer NOT NULL DEFAULT 60, + projector_countdown_warning_time integer NOT NULL CONSTRAINT minimum_projector_countdown_warning_time CHECK (projector_countdown_warning_time >= 0) DEFAULT 0, + export_csv_encoding enum_meeting_export_csv_encoding DEFAULT 'utf-8', + export_csv_separator varchar(256) DEFAULT ';', + export_pdf_pagenumber_alignment enum_meeting_export_pdf_pagenumber_alignment DEFAULT 'center', + export_pdf_fontsize enum_meeting_export_pdf_fontsize DEFAULT '10', + export_pdf_line_height real CONSTRAINT minimum_export_pdf_line_height CHECK (export_pdf_line_height >= 1.0) DEFAULT 1.25, + export_pdf_page_margin_left integer CONSTRAINT minimum_export_pdf_page_margin_left CHECK (export_pdf_page_margin_left >= 0) DEFAULT 20, + export_pdf_page_margin_top integer CONSTRAINT minimum_export_pdf_page_margin_top CHECK (export_pdf_page_margin_top >= 0) DEFAULT 25, + export_pdf_page_margin_right integer CONSTRAINT minimum_export_pdf_page_margin_right CHECK (export_pdf_page_margin_right >= 0) DEFAULT 20, + export_pdf_page_margin_bottom integer CONSTRAINT minimum_export_pdf_page_margin_bottom CHECK (export_pdf_page_margin_bottom >= 0) DEFAULT 20, + export_pdf_pagesize enum_meeting_export_pdf_pagesize DEFAULT 'A4', + agenda_show_subtitles boolean DEFAULT False, + agenda_enable_numbering boolean DEFAULT True, + agenda_number_prefix varchar(20), + agenda_numeral_system enum_meeting_agenda_numeral_system DEFAULT 'arabic', + agenda_item_creation enum_meeting_agenda_item_creation DEFAULT 'default_no', + agenda_new_items_default_visibility enum_meeting_agenda_new_items_default_visibility DEFAULT 'internal', + agenda_show_internal_items_on_projector boolean DEFAULT False, + list_of_speakers_amount_last_on_projector integer CONSTRAINT minimum_list_of_speakers_amount_last_on_projector CHECK (list_of_speakers_amount_last_on_projector >= -1) DEFAULT 0, + list_of_speakers_amount_next_on_projector integer CONSTRAINT minimum_list_of_speakers_amount_next_on_projector CHECK (list_of_speakers_amount_next_on_projector >= -1) DEFAULT -1, + list_of_speakers_couple_countdown boolean DEFAULT True, + list_of_speakers_show_amount_of_speakers_on_slide boolean DEFAULT True, + list_of_speakers_present_users_only boolean DEFAULT False, + list_of_speakers_show_first_contribution boolean DEFAULT False, + list_of_speakers_enable_point_of_order_speakers boolean DEFAULT True, + list_of_speakers_enable_point_of_order_categories boolean DEFAULT False, + list_of_speakers_closing_disables_point_of_order boolean DEFAULT False, + list_of_speakers_enable_pro_contra_speech boolean DEFAULT False, + list_of_speakers_can_set_contribution_self boolean DEFAULT False, + list_of_speakers_speaker_note_for_everyone boolean DEFAULT True, + list_of_speakers_initially_closed boolean DEFAULT False, + motions_default_workflow_id integer NOT NULL, + motions_default_amendment_workflow_id integer NOT NULL, + motions_default_statute_amendment_workflow_id integer NOT NULL, + motions_preamble text DEFAULT 'The assembly may decide:', + motions_default_line_numbering enum_meeting_motions_default_line_numbering DEFAULT 'outside', + motions_line_length integer CONSTRAINT minimum_motions_line_length CHECK (motions_line_length >= 40) DEFAULT 85, + motions_reason_required boolean DEFAULT False, + motions_enable_text_on_projector boolean DEFAULT True, + motions_enable_reason_on_projector boolean DEFAULT False, + motions_enable_sidebox_on_projector boolean DEFAULT False, + motions_enable_recommendation_on_projector boolean DEFAULT True, + motions_show_referring_motions boolean DEFAULT True, + motions_show_sequential_number boolean DEFAULT True, + motions_recommendations_by varchar(256), + motions_block_slide_columns integer CONSTRAINT minimum_motions_block_slide_columns CHECK (motions_block_slide_columns >= 1), + motions_statute_recommendations_by varchar(256), + motions_recommendation_text_mode enum_meeting_motions_recommendation_text_mode DEFAULT 'diff', + motions_default_sorting enum_meeting_motions_default_sorting DEFAULT 'number', + motions_number_type enum_meeting_motions_number_type DEFAULT 'per_category', + motions_number_min_digits integer DEFAULT 2, + motions_number_with_blank boolean DEFAULT False, + motions_statutes_enabled boolean DEFAULT False, + motions_amendments_enabled boolean DEFAULT True, + motions_amendments_in_main_list boolean DEFAULT True, + motions_amendments_of_amendments boolean DEFAULT False, + motions_amendments_prefix varchar(256) DEFAULT '-Ä', + motions_amendments_text_mode enum_meeting_motions_amendments_text_mode DEFAULT 'paragraph', + motions_amendments_multiple_paragraphs boolean DEFAULT True, + motions_supporters_min_amount integer CONSTRAINT minimum_motions_supporters_min_amount CHECK (motions_supporters_min_amount >= 0) DEFAULT 0, + motions_enable_editor boolean, + motions_enable_working_group_speaker boolean, + motions_export_title varchar(256) DEFAULT 'Motions', + motions_export_preamble text, + motions_export_submitter_recommendation boolean DEFAULT True, + motions_export_follow_recommendation boolean DEFAULT False, + motion_poll_ballot_paper_selection enum_meeting_motion_poll_ballot_paper_selection DEFAULT 'CUSTOM_NUMBER', + motion_poll_ballot_paper_number integer DEFAULT 8, + motion_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', + motion_poll_default_onehundred_percent_base enum_meeting_motion_poll_default_onehundred_percent_base DEFAULT 'YNA', + motion_poll_default_backend enum_meeting_motion_poll_default_backend DEFAULT 'fast', + users_enable_presence_view boolean DEFAULT False, + users_enable_vote_weight boolean DEFAULT False, + users_allow_self_set_present boolean DEFAULT True, + users_pdf_welcometitle varchar(256) DEFAULT 'Welcome to OpenSlides', + users_pdf_welcometext text DEFAULT '[Place for your welcome and help text.]', + users_pdf_wlan_ssid varchar(256), + users_pdf_wlan_password varchar(256), + users_pdf_wlan_encryption enum_meeting_users_pdf_wlan_encryption DEFAULT 'WPA', + users_email_sender varchar(256) DEFAULT 'OpenSlides', + users_email_replyto varchar(256), + users_email_subject varchar(256) DEFAULT 'OpenSlides access data', + users_email_body text DEFAULT 'Dear {name}, + +this is your personal OpenSlides login: + +{url} +Username: {username} +Password: {password} + + +This email was generated automatically.', + users_enable_vote_delegations boolean, + assignments_export_title varchar(256) DEFAULT 'Elections', + assignments_export_preamble text, + assignment_poll_ballot_paper_selection enum_meeting_assignment_poll_ballot_paper_selection DEFAULT 'CUSTOM_NUMBER', + assignment_poll_ballot_paper_number integer DEFAULT 8, + assignment_poll_add_candidates_to_list_of_speakers boolean DEFAULT False, + assignment_poll_enable_max_votes_per_option boolean DEFAULT False, + assignment_poll_sort_poll_result_by_votes boolean DEFAULT True, + assignment_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', + assignment_poll_default_method varchar(256) DEFAULT 'Y', + assignment_poll_default_onehundred_percent_base enum_meeting_assignment_poll_default_onehundred_percent_base DEFAULT 'valid', + assignment_poll_default_backend enum_meeting_assignment_poll_default_backend DEFAULT 'fast', + poll_ballot_paper_selection enum_meeting_poll_ballot_paper_selection, + poll_ballot_paper_number integer, + poll_sort_poll_result_by_votes boolean, + poll_default_type varchar(256) DEFAULT 'analog', + poll_default_method varchar(256), + poll_default_onehundred_percent_base enum_meeting_poll_default_onehundred_percent_base DEFAULT 'YNA', + poll_default_backend enum_meeting_poll_default_backend DEFAULT 'fast', + poll_couple_countdown boolean DEFAULT True, + logo_projector_main_id integer, + logo_projector_header_id integer, + logo_web_header_id integer, + logo_pdf_header_l_id integer, + logo_pdf_header_r_id integer, + logo_pdf_footer_l_id integer, + logo_pdf_footer_r_id integer, + logo_pdf_ballot_paper_id integer, + font_regular_id integer, + font_italic_id integer, + font_bold_id integer, + font_bold_italic_id integer, + font_monospace_id integer, + font_chyron_speaker_name_id integer, + font_projector_h1_id integer, + font_projector_h2_id integer, + committee_id integer NOT NULL, + user_ids integer[], + reference_projector_id integer NOT NULL, + list_of_speakers_countdown_id integer, + poll_countdown_id integer, + default_group_id integer NOT NULL, + admin_group_id integer +); + + + +comment on column meetingT.external_id is 'unique in committee'; +comment on column meetingT.is_active_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column meetingT.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column meetingT.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; + + +CREATE TABLE IF NOT EXISTS groupT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + external_id varchar(256), + name varchar(256) NOT NULL, + permissions enum_group_permissions[], + weight integer, + used_as_motion_poll_default_id integer, + used_as_assignment_poll_default_id integer, + used_as_topic_poll_default_id integer, + used_as_poll_default_id integer, + meeting_id integer NOT NULL +); + + + +comment on column groupT.external_id is 'unique in meeting'; + + +CREATE TABLE IF NOT EXISTS personal_noteT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + note text, + star boolean, + meeting_user_id integer NOT NULL, + content_object_id varchar(100), + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion')), + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS tagT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS agenda_itemT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + item_number varchar(256), + comment varchar(256), + closed boolean DEFAULT False, + type enum_agenda_item_type DEFAULT 'common', + duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), + is_internal boolean, + is_hidden boolean, + level integer, + weight integer, + content_object_id varchar(100) NOT NULL, + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic')), + parent_id integer, + meeting_id integer NOT NULL +); + + + +comment on column agenda_itemT.duration is 'Given in seconds'; +comment on column agenda_itemT.is_internal is 'Calculated by the server'; +comment on column agenda_itemT.is_hidden is 'Calculated by the server'; +comment on column agenda_itemT.level is 'Calculated by the server'; + + +CREATE TABLE IF NOT EXISTS list_of_speakersT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + closed boolean DEFAULT False, + sequential_number integer NOT NULL, + content_object_id varchar(100) NOT NULL, + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','mediafile')), + meeting_id integer NOT NULL +); + + + +comment on column list_of_speakersT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS point_of_order_categoryT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + text varchar(256) NOT NULL, + rank integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS speakerT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + begin_time timestamptz, + end_time timestamptz, + weight integer DEFAULT 10000, + speech_state enum_speaker_speech_state, + note varchar(250), + point_of_order boolean, + list_of_speakers_id integer NOT NULL, + meeting_user_id integer, + point_of_order_category_id integer, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS topicT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256) NOT NULL, + text text, + sequential_number integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column topicT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motionT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + number varchar(256), + number_value integer, + sequential_number integer NOT NULL, + title varchar(256) NOT NULL, + text text, + amendment_paragraphs jsonb, + modified_final_version text, + reason text, + category_weight integer DEFAULT 10000, + state_extension varchar(256), + recommendation_extension varchar(256), + sort_weight integer DEFAULT 10000, + created timestamptz, + last_modified timestamptz, + workflow_timestamp timestamptz, + start_line_number integer CONSTRAINT minimum_start_line_number CHECK (start_line_number >= 1) DEFAULT 1, + forwarded timestamptz, + lead_motion_id integer, + sort_parent_id integer, + origin_id integer, + origin_meeting_id integer, + state_id integer NOT NULL, + recommendation_id integer, + category_id integer, + block_id integer, + editor_id integer, + working_group_speaker_id integer, + statute_paragraph_id integer, + meeting_id integer NOT NULL +); + + + +comment on column motionT.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; +comment on column motionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motion_submitterT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight integer, + meeting_user_id integer NOT NULL, + motion_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS motion_commentT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + comment text, + motion_id integer NOT NULL, + section_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS motion_comment_sectionT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + weight integer DEFAULT 10000, + sequential_number integer NOT NULL, + submitter_can_write boolean, + meeting_id integer NOT NULL +); + + + +comment on column motion_comment_sectionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motion_categoryT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + prefix varchar(256), + weight integer DEFAULT 10000, + level integer, + sequential_number integer NOT NULL, + parent_id integer, + meeting_id integer NOT NULL +); + + + +comment on column motion_categoryT.level is 'Calculated field.'; +comment on column motion_categoryT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motion_blockT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256) NOT NULL, + internal boolean, + sequential_number integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column motion_blockT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motion_change_recommendationT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + rejected boolean DEFAULT False, + internal boolean DEFAULT False, + type enum_motion_change_recommendation_type DEFAULT 'replacement', + other_description varchar(256), + line_from integer CONSTRAINT minimum_line_from CHECK (line_from >= 0), + line_to integer CONSTRAINT minimum_line_to CHECK (line_to >= 0), + text text, + creation_time timestamptz, + motion_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS motion_stateT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + weight integer NOT NULL, + recommendation_label varchar(256), + is_internal_recommendation boolean, + css_class enum_motion_state_css_class NOT NULL DEFAULT 'lightblue', + restrictions enum_motion_state_restrictions[] DEFAULT '{""}', + allow_support boolean DEFAULT False, + allow_create_poll boolean DEFAULT False, + allow_submitter_edit boolean DEFAULT False, + set_number boolean DEFAULT True, + show_state_extension_field boolean DEFAULT False, + show_recommendation_extension_field boolean DEFAULT False, + merge_amendment_into_final enum_motion_state_merge_amendment_into_final DEFAULT 'undefined', + allow_motion_forwarding boolean DEFAULT False, + set_workflow_timestamp boolean DEFAULT False, + submitter_withdraw_state_id integer, + workflow_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS motion_workflowT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + sequential_number integer NOT NULL, + first_state_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column motion_workflowT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS motion_statute_paragraphT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256) NOT NULL, + text text, + weight integer DEFAULT 10000, + sequential_number integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column motion_statute_paragraphT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS pollT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + description text, + title varchar(256) NOT NULL, + type enum_poll_type NOT NULL, + backend enum_poll_backend NOT NULL DEFAULT 'fast', + is_pseudoanonymized boolean, + pollmethod enum_poll_pollmethod NOT NULL, + state enum_poll_state DEFAULT 'created', + min_votes_amount integer CONSTRAINT minimum_min_votes_amount CHECK (min_votes_amount >= 1) DEFAULT 1, + max_votes_amount integer CONSTRAINT minimum_max_votes_amount CHECK (max_votes_amount >= 1) DEFAULT 1, + max_votes_per_option integer CONSTRAINT minimum_max_votes_per_option CHECK (max_votes_per_option >= 1) DEFAULT 1, + global_yes boolean DEFAULT False, + global_no boolean DEFAULT False, + global_abstain boolean DEFAULT False, + onehundred_percent_base enum_poll_onehundred_percent_base NOT NULL DEFAULT 'disabled', + votesvalid decimal(6), + votesinvalid decimal(6), + votescast decimal(6), + entitled_users_at_stop jsonb, + sequential_number integer NOT NULL, + crypt_key varchar(256), + crypt_signature varchar(256), + votes_raw text, + votes_signature varchar(256), + content_object_id varchar(100) NOT NULL, + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), + global_option_id integer, + meeting_id integer NOT NULL +); + + + +comment on column pollT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column pollT.crypt_key is 'base64 public key to cryptographic votes.'; +comment on column pollT.crypt_signature is 'base64 signature of cryptographic_key.'; +comment on column pollT.votes_raw is 'original form of decrypted votes.'; +comment on column pollT.votes_signature is 'base64 signature of votes_raw field.'; + +/* + Fields without SQL definition for table poll + + vote_count type:number is marked as a calculated field + +*/ + +CREATE TABLE IF NOT EXISTS optionT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight integer DEFAULT 10000, + text text, + yes decimal(6), + no decimal(6), + abstain decimal(6), + poll_id integer, + content_object_id varchar(100), + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_candidate_list_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS voteT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight decimal(6), + value varchar(256), + user_token varchar(256) NOT NULL, + option_id integer NOT NULL, + user_id integer, + delegated_user_id integer, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS assignmentT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256) NOT NULL, + description text, + open_posts integer CONSTRAINT minimum_open_posts CHECK (open_posts >= 0) DEFAULT 0, + phase enum_assignment_phase DEFAULT 'search', + default_poll_description text, + number_poll_candidates boolean, + sequential_number integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column assignmentT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS assignment_candidateT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight integer DEFAULT 10000, + assignment_id integer NOT NULL, + meeting_user_id integer, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS poll_candidate_listT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS poll_candidateT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + poll_candidate_list_id integer NOT NULL, + user_id integer, + weight integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS mediafileT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256), + is_directory boolean, + filesize integer, + filename varchar(256), + mimetype varchar(256), + pdf_information jsonb, + create_timestamp timestamptz, + is_public boolean NOT NULL, + token varchar(256), + parent_id integer, + owner_id varchar(100) NOT NULL, + owner_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'meeting' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, + owner_id_organization_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'organization' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_owner_id_part1 CHECK (split_part(owner_id, '/', 1) IN ('meeting','organization')) +); + + + +comment on column mediafileT.title is 'Title and parent_id must be unique.'; +comment on column mediafileT.filesize is 'In bytes, not the human readable format anymore.'; +comment on column mediafileT.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; +comment on column mediafileT.is_public is 'Calculated field. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; + + +CREATE TABLE IF NOT EXISTS projectorT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256), + is_internal boolean DEFAULT False, + scale integer DEFAULT 0, + scroll integer CONSTRAINT minimum_scroll CHECK (scroll >= 0) DEFAULT 0, + width integer CONSTRAINT minimum_width CHECK (width >= 1) DEFAULT 1200, + aspect_ratio_numerator integer CONSTRAINT minimum_aspect_ratio_numerator CHECK (aspect_ratio_numerator >= 1) DEFAULT 16, + aspect_ratio_denominator integer CONSTRAINT minimum_aspect_ratio_denominator CHECK (aspect_ratio_denominator >= 1) DEFAULT 9, + color integer CHECK (color >= 0 and color <= 16777215) DEFAULT 0, + background_color integer CHECK (background_color >= 0 and background_color <= 16777215) DEFAULT 16777215, + header_background_color integer CHECK (header_background_color >= 0 and header_background_color <= 16777215) DEFAULT 3241878, + header_font_color integer CHECK (header_font_color >= 0 and header_font_color <= 16777215) DEFAULT 16119285, + header_h1_color integer CHECK (header_h1_color >= 0 and header_h1_color <= 16777215) DEFAULT 3241878, + chyron_background_color integer CHECK (chyron_background_color >= 0 and chyron_background_color <= 16777215) DEFAULT 3241878, + chyron_font_color integer CHECK (chyron_font_color >= 0 and chyron_font_color <= 16777215) DEFAULT 16777215, + show_header_footer boolean DEFAULT True, + show_title boolean DEFAULT True, + show_logo boolean DEFAULT True, + show_clock boolean DEFAULT True, + sequential_number integer NOT NULL, + meeting_id integer NOT NULL +); + + + +comment on column projectorT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE IF NOT EXISTS projectionT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + options jsonb, + stable boolean DEFAULT False, + weight integer, + type varchar(256), + current_projector_id integer, + preview_projector_id integer, + history_projector_id integer, + content_object_id varchar(100) NOT NULL, + content_object_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_list_of_speakers_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'list_of_speakers' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'agenda_item' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_projector_message_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_message' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_projector_countdown_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_countdown' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('meeting','motion','mediafile','list_of_speakers','motion_block','assignment','agenda_item','topic','poll','projector_message','projector_countdown')), + meeting_id integer NOT NULL +); + + + +/* + Fields without SQL definition for table projection + + content type:JSON is marked as a calculated field + +*/ + +CREATE TABLE IF NOT EXISTS projector_messageT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + message text, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS projector_countdownT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + title varchar(256) NOT NULL, + description varchar(256) DEFAULT '', + default_time integer, + countdown_time real DEFAULT 60, + running boolean DEFAULT False, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS chat_groupT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + weight integer DEFAULT 10000, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS chat_messageT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + content text NOT NULL, + created timestamptz NOT NULL, + meeting_user_id integer NOT NULL, + chat_group_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS action_workerT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(256) NOT NULL, + state enum_action_worker_state NOT NULL, + created timestamptz NOT NULL, + timestamp timestamptz NOT NULL, + result jsonb +); + + + + +CREATE TABLE IF NOT EXISTS import_previewT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name enum_import_preview_name NOT NULL, + state enum_import_preview_state NOT NULL, + created timestamptz NOT NULL, + result jsonb +); + + + + + +-- Intermediate table definitions + +CREATE TABLE IF NOT EXISTS nm_meeting_user_supported_motion_ids_motionT ( + meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), + motion_id integer NOT NULL REFERENCES motionT (id), + PRIMARY KEY (meeting_user_id, motion_id) +); + +CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_idsT ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + organization_tag_id integer NOT NULL REFERENCES organization_tagT(id), + tagged_id varchar(100) NOT NULL, + tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committeeT(id), + tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meetingT(id), + CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), + CONSTRAINT unique_organization_tag_id_tagged_id UNIQUE (organization_tag_id, tagged_id) +); + +CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_userT ( + committee_id integer NOT NULL REFERENCES committeeT (id), + user_id integer NOT NULL REFERENCES userT (id), + PRIMARY KEY (committee_id, user_id) +); + +CREATE TABLE IF NOT EXISTS nm_committee_forward_to_committee_ids_committeeT ( + forward_to_committee_id integer NOT NULL REFERENCES committeeT (id), + receive_forwardings_from_committee_id integer NOT NULL REFERENCES committeeT (id), + PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) +); + +CREATE TABLE IF NOT EXISTS nm_meeting_present_user_ids_userT ( + meeting_id integer NOT NULL REFERENCES meetingT (id), + user_id integer NOT NULL REFERENCES userT (id), + PRIMARY KEY (meeting_id, user_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_meeting_user_ids_meeting_userT ( + group_id integer NOT NULL REFERENCES groupT (id), + meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), + PRIMARY KEY (group_id, meeting_user_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_mediafile_access_group_ids_mediafileT ( + group_id integer NOT NULL REFERENCES groupT (id), + mediafile_id integer NOT NULL REFERENCES mediafileT (id), + PRIMARY KEY (group_id, mediafile_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_mediafile_inherited_access_group_ids_mediafileT ( + group_id integer NOT NULL REFERENCES groupT (id), + mediafile_id integer NOT NULL REFERENCES mediafileT (id), + PRIMARY KEY (group_id, mediafile_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_read_comment_section_ids_motion_comment_sectionT ( + group_id integer NOT NULL REFERENCES groupT (id), + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_sectionT (id), + PRIMARY KEY (group_id, motion_comment_section_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_write_comment_section_ids_motion_comment_sectionT ( + group_id integer NOT NULL REFERENCES groupT (id), + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_sectionT (id), + PRIMARY KEY (group_id, motion_comment_section_id) +); + +CREATE TABLE IF NOT EXISTS nm_group_poll_ids_pollT ( + group_id integer NOT NULL REFERENCES groupT (id), + poll_id integer NOT NULL REFERENCES pollT (id), + PRIMARY KEY (group_id, poll_id) +); + +CREATE TABLE IF NOT EXISTS gm_tag_tagged_idsT ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + tag_id integer NOT NULL REFERENCES tagT(id), + tagged_id varchar(100) NOT NULL, + tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_itemT(id), + tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), + tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), + CONSTRAINT unique_tag_id_tagged_id UNIQUE (tag_id, tagged_id) +); + +CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motionT ( + all_derived_motion_id integer NOT NULL REFERENCES motionT (id), + all_origin_id integer NOT NULL REFERENCES motionT (id), + PRIMARY KEY (all_derived_motion_id, all_origin_id) +); + +CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_idsT ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + motion_id integer NOT NULL REFERENCES motionT(id), + state_extension_reference_id varchar(100) NOT NULL, + state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), + CONSTRAINT unique_motion_id_state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) +); + +CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_idsT ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + motion_id integer NOT NULL REFERENCES motionT(id), + recommendation_extension_reference_id varchar(100) NOT NULL, + recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), + CONSTRAINT unique_motion_id_recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) +); + +CREATE TABLE IF NOT EXISTS nm_motion_state_next_state_ids_motion_stateT ( + next_state_id integer NOT NULL REFERENCES motion_stateT (id), + previous_state_id integer NOT NULL REFERENCES motion_stateT (id), + PRIMARY KEY (next_state_id, previous_state_id) +); + +CREATE TABLE IF NOT EXISTS nm_poll_voted_ids_userT ( + poll_id integer NOT NULL REFERENCES pollT (id), + user_id integer NOT NULL REFERENCES userT (id), + PRIMARY KEY (poll_id, user_id) +); + +CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_idsT ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + mediafile_id integer NOT NULL REFERENCES mediafileT(id), + attachment_id varchar(100) NOT NULL, + attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topicT(id), + attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), + CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), + CONSTRAINT unique_mediafile_id_attachment_id UNIQUE (mediafile_id, attachment_id) +); + +CREATE TABLE IF NOT EXISTS nm_chat_group_read_group_ids_groupT ( + chat_group_id integer NOT NULL REFERENCES chat_groupT (id), + group_id integer NOT NULL REFERENCES groupT (id), + PRIMARY KEY (chat_group_id, group_id) +); + +CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_groupT ( + chat_group_id integer NOT NULL REFERENCES chat_groupT (id), + group_id integer NOT NULL REFERENCES groupT (id), + PRIMARY KEY (chat_group_id, group_id) +); +-- View definitions + +CREATE OR REPLACE VIEW organization AS SELECT *, +(select array_agg(c.id) from committeeT c) as committee_ids, +(select array_agg(m.id) from meetingT m where m.is_active_in_organization_id = o.id) as active_meeting_ids, +(select array_agg(m.id) from meetingT m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, +(select array_agg(m.id) from meetingT m where m.template_for_organization_id = o.id) as template_meeting_ids, +(select array_agg(ot.id) from organization_tagT ot) as organization_tag_ids, +(select array_agg(t.id) from themeT t) as theme_ids, +(select array_agg(m.owner_id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(u.id) from userT u) as user_ids +FROM organizationT o; + + +CREATE OR REPLACE VIEW user_ AS SELECT *, +(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_userT n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.committee_id) from nm_committee_manager_ids_userT n where n.user_id = u.id) as committee_management_ids, +(select array_agg(c.id) from committeeT c where c.forwarding_user_id = u.id) as forwarding_committee_ids, +(select array_agg(m.id) from meeting_userT m where m.user_id = u.id) as meeting_user_ids, +(select array_agg(n.poll_id) from nm_poll_voted_ids_userT n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(o.content_object_id) from optionT o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(v.id) from voteT v where v.user_id = u.id) as vote_ids, +(select array_agg(v.id) from voteT v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(p.id) from poll_candidateT p where p.user_id = u.id) as poll_candidate_ids +FROM userT u; + + +CREATE OR REPLACE VIEW meeting_user AS SELECT *, +(select array_agg(p.id) from personal_noteT p where p.meeting_user_id = m.id) as personal_note_ids, +(select array_agg(s.id) from speakerT s where s.meeting_user_id = m.id) as speaker_ids, +(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motionT n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(m1.id) from motionT m1 where m1.editor_id = m.id) as editor_for_motion_ids, +(select array_agg(m1.id) from motionT m1 where m1.working_group_speaker_id = m.id) as working_group_speaker_for_motion_ids, +(select array_agg(ms.id) from motion_submitterT ms where ms.meeting_user_id = m.id) as motion_submitter_ids, +(select array_agg(a.id) from assignment_candidateT a where a.meeting_user_id = m.id) as assignment_candidate_ids, +(select array_agg(mu.id) from meeting_userT mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, +(select array_agg(c.id) from chat_messageT c where c.meeting_user_id = m.id) as chat_message_ids, +(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_userT n where n.meeting_user_id = m.id) as group_ids +FROM meeting_userT m; + + +CREATE OR REPLACE VIEW organization_tag AS SELECT *, +(select array_agg(g.id) from gm_organization_tag_tagged_idsT g where g.organization_tag_id = o.id) as tagged_ids +FROM organization_tagT o; + + +CREATE OR REPLACE VIEW theme AS SELECT *, +(select o.id from organizationT o where o.theme_id = t.id) as theme_for_organization_id +FROM themeT t; + + +CREATE OR REPLACE VIEW committee AS SELECT *, +(select array_agg(m.id) from meetingT m where m.committee_id = c.id) as meeting_ids, +(select array_agg(n.user_id) from nm_committee_manager_ids_userT n where n.committee_id = c.id) as manager_ids, +(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, +(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, +(select array_agg(g.tagged_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_committee_id = c.id) as organization_tag_ids +FROM committeeT c; + + +CREATE OR REPLACE VIEW meeting AS SELECT *, +(select array_agg(g.id) from groupT g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, +(select array_agg(p.id) from poll_candidate_listT p where p.meeting_id = m.id) as poll_candidate_list_ids, +(select array_agg(p.id) from poll_candidateT p where p.meeting_id = m.id) as poll_candidate_ids, +(select array_agg(mu.id) from meeting_userT mu where mu.meeting_id = m.id) as meeting_user_ids, +(select array_agg(g.id) from groupT g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, +(select array_agg(g.id) from groupT g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, +(select array_agg(g.id) from groupT g where g.used_as_topic_poll_default_id = m.id) as topic_poll_default_group_ids, +(select array_agg(p.id) from projectorT p where p.meeting_id = m.id) as projector_ids, +(select array_agg(p.id) from projectionT p where p.meeting_id = m.id) as all_projection_ids, +(select array_agg(p.id) from projector_messageT p where p.meeting_id = m.id) as projector_message_ids, +(select array_agg(p.id) from projector_countdownT p where p.meeting_id = m.id) as projector_countdown_ids, +(select array_agg(t.id) from tagT t where t.meeting_id = m.id) as tag_ids, +(select array_agg(a.id) from agenda_itemT a where a.meeting_id = m.id) as agenda_item_ids, +(select array_agg(l.id) from list_of_speakersT l where l.meeting_id = m.id) as list_of_speakers_ids, +(select array_agg(p.id) from point_of_order_categoryT p where p.meeting_id = m.id) as point_of_order_category_ids, +(select array_agg(s.id) from speakerT s where s.meeting_id = m.id) as speaker_ids, +(select array_agg(t.id) from topicT t where t.meeting_id = m.id) as topic_ids, +(select array_agg(g.id) from groupT g where g.meeting_id = m.id) as group_ids, +(select array_agg(m1.owner_id) from mediafileT m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, +(select array_agg(m1.id) from motionT m1 where m1.meeting_id = m.id) as motion_ids, +(select array_agg(m1.id) from motionT m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, +(select array_agg(mc.id) from motion_comment_sectionT mc where mc.meeting_id = m.id) as motion_comment_section_ids, +(select array_agg(mc.id) from motion_categoryT mc where mc.meeting_id = m.id) as motion_category_ids, +(select array_agg(mb.id) from motion_blockT mb where mb.meeting_id = m.id) as motion_block_ids, +(select array_agg(mw.id) from motion_workflowT mw where mw.meeting_id = m.id) as motion_workflow_ids, +(select array_agg(ms.id) from motion_statute_paragraphT ms where ms.meeting_id = m.id) as motion_statute_paragraph_ids, +(select array_agg(mc.id) from motion_commentT mc where mc.meeting_id = m.id) as motion_comment_ids, +(select array_agg(ms.id) from motion_submitterT ms where ms.meeting_id = m.id) as motion_submitter_ids, +(select array_agg(mc.id) from motion_change_recommendationT mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, +(select array_agg(ms.id) from motion_stateT ms where ms.meeting_id = m.id) as motion_state_ids, +(select array_agg(p.id) from pollT p where p.meeting_id = m.id) as poll_ids, +(select array_agg(o.id) from optionT o where o.meeting_id = m.id) as option_ids, +(select array_agg(v.id) from voteT v where v.meeting_id = m.id) as vote_ids, +(select array_agg(a.id) from assignmentT a where a.meeting_id = m.id) as assignment_ids, +(select array_agg(a.id) from assignment_candidateT a where a.meeting_id = m.id) as assignment_candidate_ids, +(select array_agg(p.id) from personal_noteT p where p.meeting_id = m.id) as personal_note_ids, +(select array_agg(c.id) from chat_groupT c where c.meeting_id = m.id) as chat_group_ids, +(select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, +(select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, +(select array_agg(g.tagged_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids +FROM meetingT m; + + +CREATE OR REPLACE VIEW group_ AS SELECT *, +(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_userT n where n.group_id = g.id) as meeting_user_ids, +(select m.id from meetingT m where m.default_group_id = g.id) as default_group_for_meeting_id, +(select m.id from meetingT m where m.admin_group_id = g.id) as admin_group_for_meeting_id, +(select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_access_group_ids, +(select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_groupT n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_groupT n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id) from nm_group_poll_ids_pollT n where n.group_id = g.id) as poll_ids +FROM groupT g; + + +CREATE OR REPLACE VIEW tag AS SELECT *, +(select array_agg(g.id) from gm_tag_tagged_idsT g where g.tag_id = t.id) as tagged_ids +FROM tagT t; + + +CREATE OR REPLACE VIEW agenda_item AS SELECT *, +(select array_agg(ai.id) from agenda_itemT ai where ai.parent_id = a.id) as child_ids, +(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_agenda_item_id = a.id) as projection_ids +FROM agenda_itemT a; + + +CREATE OR REPLACE VIEW list_of_speakers AS SELECT *, +(select array_agg(s.id) from speakerT s where s.list_of_speakers_id = l.id) as speaker_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids +FROM list_of_speakersT l; + + +CREATE OR REPLACE VIEW point_of_order_category AS SELECT *, +(select array_agg(s.id) from speakerT s where s.point_of_order_category_id = p.id) as speaker_ids +FROM point_of_order_categoryT p; + + +CREATE OR REPLACE VIEW topic AS SELECT *, +(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_topic_id = t.id) as attachment_ids, +(select a.id from agenda_itemT a where a.content_object_id_topic_id = t.id) as agenda_item_id, +(select l.id from list_of_speakersT l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, +(select array_agg(p.content_object_id) from pollT p where p.content_object_id_topic_id = t.id) as poll_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_topic_id = t.id) as projection_ids +FROM topicT t; + + +CREATE OR REPLACE VIEW motion AS SELECT *, +(select array_agg(m1.id) from motionT m1 where m1.lead_motion_id = m.id) as amendment_ids, +(select array_agg(m1.id) from motionT m1 where m1.sort_parent_id = m.id) as sort_child_ids, +(select array_agg(m1.id) from motionT m1 where m1.origin_id = m.id) as derived_motion_ids, +(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_derived_motion_id = m.id) as all_origin_ids, +(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_agg(g.id) from gm_motion_state_extension_reference_idsT g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.state_extension_reference_id) from gm_motion_state_extension_reference_idsT g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.id) from gm_motion_recommendation_extension_reference_idsT g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_idsT g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(ms.id) from motion_submitterT ms where ms.motion_id = m.id) as submitter_ids, +(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motionT n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(p.content_object_id) from pollT p where p.content_object_id_motion_id = m.id) as poll_ids, +(select array_agg(o.content_object_id) from optionT o where o.content_object_id_motion_id = m.id) as option_ids, +(select array_agg(mc.id) from motion_change_recommendationT mc where mc.motion_id = m.id) as change_recommendation_ids, +(select array_agg(mc.id) from motion_commentT mc where mc.motion_id = m.id) as comment_ids, +(select a.id from agenda_itemT a where a.content_object_id_motion_id = m.id) as agenda_item_id, +(select l.id from list_of_speakersT l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, +(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_motion_id = m.id) as attachment_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_motion_id = m.id) as projection_ids, +(select array_agg(p.content_object_id) from personal_noteT p where p.content_object_id_motion_id = m.id) as personal_note_ids +FROM motionT m; + + +CREATE OR REPLACE VIEW motion_comment_section AS SELECT *, +(select array_agg(mc.id) from motion_commentT mc where mc.section_id = m.id) as comment_ids, +(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.motion_comment_section_id = m.id) as read_group_ids, +(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_sectionT n where n.motion_comment_section_id = m.id) as write_group_ids +FROM motion_comment_sectionT m; + + +CREATE OR REPLACE VIEW motion_category AS SELECT *, +(select array_agg(mc.id) from motion_categoryT mc where mc.parent_id = m.id) as child_ids, +(select array_agg(m1.id) from motionT m1 where m1.category_id = m.id) as motion_ids +FROM motion_categoryT m; + + +CREATE OR REPLACE VIEW motion_block AS SELECT *, +(select array_agg(m1.id) from motionT m1 where m1.block_id = m.id) as motion_ids, +(select a.id from agenda_itemT a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, +(select l.id from list_of_speakersT l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_motion_block_id = m.id) as projection_ids +FROM motion_blockT m; + + +CREATE OR REPLACE VIEW motion_state AS SELECT *, +(select array_agg(ms.id) from motion_stateT ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, +(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_stateT n where n.previous_state_id = m.id) as next_state_ids, +(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_stateT n where n.next_state_id = m.id) as previous_state_ids, +(select array_agg(m1.id) from motionT m1 where m1.state_id = m.id) as motion_ids, +(select array_agg(m1.id) from motionT m1 where m1.recommendation_id = m.id) as motion_recommendation_ids, +(select mw.id from motion_workflowT mw where mw.first_state_id = m.id) as first_state_of_workflow_id +FROM motion_stateT m; + + +CREATE OR REPLACE VIEW motion_workflow AS SELECT *, +(select array_agg(ms.id) from motion_stateT ms where ms.workflow_id = m.id) as state_ids, +(select m1.id from meetingT m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, +(select m1.id from meetingT m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id, +(select m1.id from meetingT m1 where m1.motions_default_statute_amendment_workflow_id = m.id) as default_statute_amendment_workflow_meeting_id +FROM motion_workflowT m; + + +CREATE OR REPLACE VIEW motion_statute_paragraph AS SELECT *, +(select array_agg(m1.id) from motionT m1 where m1.statute_paragraph_id = m.id) as motion_ids +FROM motion_statute_paragraphT m; + + +CREATE OR REPLACE VIEW poll AS SELECT *, +(select array_agg(o.id) from optionT o where o.poll_id = p.id) as option_ids, +(select array_agg(n.user_id) from nm_poll_voted_ids_userT n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id) from nm_group_poll_ids_pollT n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_poll_id = p.id) as projection_ids +FROM pollT p; + + +CREATE OR REPLACE VIEW option AS SELECT *, +(select p.id from pollT p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, +(select array_agg(v.id) from voteT v where v.option_id = o.id) as vote_ids +FROM optionT o; + + +CREATE OR REPLACE VIEW assignment AS SELECT *, +(select array_agg(ac.id) from assignment_candidateT ac where ac.assignment_id = a.id) as candidate_ids, +(select array_agg(p.content_object_id) from pollT p where p.content_object_id_assignment_id = a.id) as poll_ids, +(select ai.id from agenda_itemT ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, +(select l.id from list_of_speakersT l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, +(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_assignment_id = a.id) as attachment_ids, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_assignment_id = a.id) as projection_ids +FROM assignmentT a; + + +CREATE OR REPLACE VIEW poll_candidate_list AS SELECT *, +(select array_agg(pc.id) from poll_candidateT pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, +(select o.id from optionT o where o.content_object_id_poll_candidate_list_id = p.id) as option_id +FROM poll_candidate_listT p; + + +CREATE OR REPLACE VIEW mediafile AS SELECT *, +(select array_agg(n.group_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.mediafile_id = m.id) as access_group_ids, +(select array_agg(m1.id) from mediafileT m1 where m1.parent_id = m.id) as child_ids, +(select l.id from list_of_speakersT l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, +(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, +(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids, +(select m1.id from meetingT m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id +FROM mediafileT m; + + +CREATE OR REPLACE VIEW projector AS SELECT *, +(select array_agg(p1.id) from projectionT p1 where p1.current_projector_id = p.id) as current_projection_ids, +(select array_agg(p1.id) from projectionT p1 where p1.preview_projector_id = p.id) as preview_projection_ids, +(select array_agg(p1.id) from projectionT p1 where p1.history_projector_id = p.id) as history_projection_ids, +(select m.id from meetingT m where m.reference_projector_id = p.id) as used_as_reference_projector_meeting_id +FROM projectorT p; + + +CREATE OR REPLACE VIEW projector_message AS SELECT *, +(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids +FROM projector_messageT p; + + +CREATE OR REPLACE VIEW projector_countdown AS SELECT *, +(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select m.id from meetingT m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, +(select m.id from meetingT m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id +FROM projector_countdownT p; + + +CREATE OR REPLACE VIEW chat_group AS SELECT *, +(select array_agg(cm.id) from chat_messageT cm where cm.chat_group_id = c.id) as chat_message_ids, +(select array_agg(n.group_id) from nm_chat_group_read_group_ids_groupT n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id) from nm_chat_group_write_group_ids_groupT n where n.chat_group_id = c.id) as write_group_ids +FROM chat_groupT c; + +-- Alter table relations +ALTER TABLE organizationT ADD FOREIGN KEY(theme_id) REFERENCES themeT(id); + +ALTER TABLE meeting_userT ADD FOREIGN KEY(user_id) REFERENCES userT(id); +ALTER TABLE meeting_userT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +ALTER TABLE meeting_userT ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_userT(id); + +ALTER TABLE committeeT ADD FOREIGN KEY(default_meeting_id) REFERENCES meetingT(id); +ALTER TABLE committeeT ADD FOREIGN KEY(forwarding_user_id) REFERENCES userT(id); + +ALTER TABLE meetingT ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organizationT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organizationT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(template_for_organization_id) REFERENCES organizationT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_regular_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_italic_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(committee_id) REFERENCES committeeT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(reference_projector_id) REFERENCES projectorT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdownT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdownT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(default_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(admin_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; + +ALTER TABLE groupT ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE groupT ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE groupT ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE groupT ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE groupT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; + +ALTER TABLE personal_noteT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE personal_noteT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE personal_noteT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE tagT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); +ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); +ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); +ALTER TABLE agenda_itemT ADD FOREIGN KEY(parent_id) REFERENCES agenda_itemT(id); +ALTER TABLE agenda_itemT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafileT(id); +ALTER TABLE list_of_speakersT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE point_of_order_categoryT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE speakerT ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakersT(id); +ALTER TABLE speakerT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE speakerT ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_categoryT(id); +ALTER TABLE speakerT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE topicT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motionT ADD FOREIGN KEY(lead_motion_id) REFERENCES motionT(id); +ALTER TABLE motionT ADD FOREIGN KEY(sort_parent_id) REFERENCES motionT(id); +ALTER TABLE motionT ADD FOREIGN KEY(origin_id) REFERENCES motionT(id); +ALTER TABLE motionT ADD FOREIGN KEY(origin_meeting_id) REFERENCES meetingT(id); +ALTER TABLE motionT ADD FOREIGN KEY(state_id) REFERENCES motion_stateT(id); +ALTER TABLE motionT ADD FOREIGN KEY(recommendation_id) REFERENCES motion_stateT(id); +ALTER TABLE motionT ADD FOREIGN KEY(category_id) REFERENCES motion_categoryT(id); +ALTER TABLE motionT ADD FOREIGN KEY(block_id) REFERENCES motion_blockT(id); +ALTER TABLE motionT ADD FOREIGN KEY(editor_id) REFERENCES meeting_userT(id); +ALTER TABLE motionT ADD FOREIGN KEY(working_group_speaker_id) REFERENCES meeting_userT(id); +ALTER TABLE motionT ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraphT(id); +ALTER TABLE motionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE motion_submitterT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); +ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_commentT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); +ALTER TABLE motion_commentT ADD FOREIGN KEY(section_id) REFERENCES motion_comment_sectionT(id); +ALTER TABLE motion_commentT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_comment_sectionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_categoryT ADD FOREIGN KEY(parent_id) REFERENCES motion_categoryT(id); +ALTER TABLE motion_categoryT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_blockT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_change_recommendationT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); +ALTER TABLE motion_change_recommendationT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_stateT ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_stateT(id); +ALTER TABLE motion_stateT ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; +ALTER TABLE motion_stateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_workflowT ADD FOREIGN KEY(first_state_id) REFERENCES motion_stateT(id) INITIALLY DEFERRED; +ALTER TABLE motion_workflowT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; + +ALTER TABLE motion_statute_paragraphT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); +ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); +ALTER TABLE pollT ADD FOREIGN KEY(global_option_id) REFERENCES optionT(id); +ALTER TABLE pollT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE optionT ADD FOREIGN KEY(poll_id) REFERENCES pollT(id); +ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_user_id) REFERENCES userT(id); +ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_listT(id); +ALTER TABLE optionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE voteT ADD FOREIGN KEY(option_id) REFERENCES optionT(id); +ALTER TABLE voteT ADD FOREIGN KEY(user_id) REFERENCES userT(id); +ALTER TABLE voteT ADD FOREIGN KEY(delegated_user_id) REFERENCES userT(id); +ALTER TABLE voteT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE assignmentT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE assignment_candidateT ADD FOREIGN KEY(assignment_id) REFERENCES assignmentT(id); +ALTER TABLE assignment_candidateT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE assignment_candidateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE poll_candidate_listT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE poll_candidateT ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_listT(id); +ALTER TABLE poll_candidateT ADD FOREIGN KEY(user_id) REFERENCES userT(id); +ALTER TABLE poll_candidateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE mediafileT ADD FOREIGN KEY(parent_id) REFERENCES mediafileT(id); +ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meetingT(id); +ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organizationT(id); + +ALTER TABLE projectorT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; + +ALTER TABLE projectionT ADD FOREIGN KEY(current_projector_id) REFERENCES projectorT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(preview_projector_id) REFERENCES projectorT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(history_projector_id) REFERENCES projectorT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meetingT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafileT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakersT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_itemT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES pollT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_messageT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdownT(id); +ALTER TABLE projectionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE projector_messageT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE projector_countdownT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE chat_groupT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE chat_messageT ADD FOREIGN KEY(chat_group_id) REFERENCES chat_groupT(id); +ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + + +/* Relation-list infos +Generated: What will be generated for left field + FIELD: a usual Database field + SQL: a sql-expression in a view + NOTHING: still nothing + ***: Error +Field Attributes:Field Attributes opposite side + 1: cardinality 1 + 1G: cardinality 1 with generic-relation field + n: cardinality n + nG: cardinality n with generic-relation-list field + t: "to" defined + r: "reference" defined + s: sql directive given, but must be generated + s+: sql directive includive sql-statement + R: Required + p: primary set for deciding field/sql +Model.Field -> Model.Field + model.field names +*/ + +/* +ns+: => organization/committee_ids:-> - +SQL nt:1t => organization/active_meeting_ids:-> meeting/is_active_in_organization_id +SQL nt:1t => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id +SQL nt:1t => organization/template_meeting_ids:-> meeting/template_for_organization_id +SQL nr: => organization/organization_tag_ids:-> organization_tag/ +FIELD 1rR: => organization/theme_id:-> theme/ +SQL nr: => organization/theme_ids:-> theme/ +SQL nt:1GtR => organization/mediafile_ids:-> mediafile/owner_id +SQL nr: => organization/user_ids:-> user/ + +SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids +SQL nt:nt => user/committee_management_ids:-> committee/manager_ids +SQL nt:1t => user/forwarding_committee_ids:-> committee/forwarding_user_id +SQL nt:1tR => user/meeting_user_ids:-> meeting_user/user_id +SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids +SQL nt:1Gt => user/option_ids:-> option/content_object_id +SQL nt:1t => user/vote_ids:-> vote/user_id +SQL nt:1t => user/delegated_vote_ids:-> vote/delegated_user_id +SQL nt:1t => user/poll_candidate_ids:-> poll_candidate/user_id + +FIELD 1tR:nt => meeting_user/user_id:-> user/meeting_user_ids +FIELD 1tR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids +SQL nt:1tR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id +SQL nt:1t => meeting_user/speaker_ids:-> speaker/meeting_user_id +SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids +SQL nt:1t => meeting_user/editor_for_motion_ids:-> motion/editor_id +SQL nt:1t => meeting_user/working_group_speaker_for_motion_ids:-> motion/working_group_speaker_id +SQL nt:1tR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id +SQL nt:1t => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id +FIELD 1t:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids +SQL nt:1t => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id +SQL nt:1tR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id +SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids + +FIELD nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids + +SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id + +SQL nt:1tR => committee/meeting_ids:-> meeting/committee_id +FIELD 1tp:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id +SQL nt:nt => committee/manager_ids:-> user/committee_management_ids +SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids +SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids +FIELD 1t:nt => committee/forwarding_user_id:-> user/forwarding_committee_ids +SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids + +FIELD 1t:nt => meeting/is_active_in_organization_id:-> organization/active_meeting_ids +FIELD 1t:nt => meeting/is_archived_in_organization_id:-> organization/archived_meeting_ids +FIELD 1t:nt => meeting/template_for_organization_id:-> organization/template_meeting_ids +FIELD 1tR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id +FIELD 1tR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id +FIELD 1tR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id +SQL nt:1t => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id +SQL nt:1tR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id +SQL nt:1tR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id +SQL nt:1tR => meeting/meeting_user_ids:-> meeting_user/meeting_id +SQL nt:1t => meeting/assignment_poll_default_group_ids:-> group/used_as_assignment_poll_default_id +SQL nt:1t => meeting/poll_default_group_ids:-> group/used_as_poll_default_id +SQL nt:1t => meeting/topic_poll_default_group_ids:-> group/used_as_topic_poll_default_id +SQL nt:1tR => meeting/projector_ids:-> projector/meeting_id +SQL nt:1tR => meeting/all_projection_ids:-> projection/meeting_id +SQL nt:1tR => meeting/projector_message_ids:-> projector_message/meeting_id +SQL nt:1tR => meeting/projector_countdown_ids:-> projector_countdown/meeting_id +SQL nt:1tR => meeting/tag_ids:-> tag/meeting_id +SQL nt:1tR => meeting/agenda_item_ids:-> agenda_item/meeting_id +SQL nt:1tR => meeting/list_of_speakers_ids:-> list_of_speakers/meeting_id +SQL nt:1tR => meeting/point_of_order_category_ids:-> point_of_order_category/meeting_id +SQL nt:1tR => meeting/speaker_ids:-> speaker/meeting_id +SQL nt:1tR => meeting/topic_ids:-> topic/meeting_id +SQL nt:1tR => meeting/group_ids:-> group/meeting_id +SQL nt:1GtR => meeting/mediafile_ids:-> mediafile/owner_id +SQL nt:1tR => meeting/motion_ids:-> motion/meeting_id +SQL nt:1t => meeting/forwarded_motion_ids:-> motion/origin_meeting_id +SQL nt:1tR => meeting/motion_comment_section_ids:-> motion_comment_section/meeting_id +SQL nt:1tR => meeting/motion_category_ids:-> motion_category/meeting_id +SQL nt:1tR => meeting/motion_block_ids:-> motion_block/meeting_id +SQL nt:1tR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id +SQL nt:1tR => meeting/motion_statute_paragraph_ids:-> motion_statute_paragraph/meeting_id +SQL nt:1tR => meeting/motion_comment_ids:-> motion_comment/meeting_id +SQL nt:1tR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id +SQL nt:1tR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id +SQL nt:1tR => meeting/motion_state_ids:-> motion_state/meeting_id +SQL nt:1tR => meeting/poll_ids:-> poll/meeting_id +SQL nt:1tR => meeting/option_ids:-> option/meeting_id +SQL nt:1tR => meeting/vote_ids:-> vote/meeting_id +SQL nt:1tR => meeting/assignment_ids:-> assignment/meeting_id +SQL nt:1tR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id +SQL nt:1tR => meeting/personal_note_ids:-> personal_note/meeting_id +SQL nt:1tR => meeting/chat_group_ids:-> chat_group/meeting_id +SQL nt:1tR => meeting/chat_message_ids:-> chat_message/meeting_id +FIELD 1tp:1t => meeting/logo_projector_main_id:-> mediafile/used_as_logo_projector_main_in_meeting_id +FIELD 1tp:1t => meeting/logo_projector_header_id:-> mediafile/used_as_logo_projector_header_in_meeting_id +FIELD 1tp:1t => meeting/logo_web_header_id:-> mediafile/used_as_logo_web_header_in_meeting_id +FIELD 1tp:1t => meeting/logo_pdf_header_l_id:-> mediafile/used_as_logo_pdf_header_l_in_meeting_id +FIELD 1tp:1t => meeting/logo_pdf_header_r_id:-> mediafile/used_as_logo_pdf_header_r_in_meeting_id +FIELD 1tp:1t => meeting/logo_pdf_footer_l_id:-> mediafile/used_as_logo_pdf_footer_l_in_meeting_id +FIELD 1tp:1t => meeting/logo_pdf_footer_r_id:-> mediafile/used_as_logo_pdf_footer_r_in_meeting_id +FIELD 1tp:1t => meeting/logo_pdf_ballot_paper_id:-> mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id +FIELD 1tp:1t => meeting/font_regular_id:-> mediafile/used_as_font_regular_in_meeting_id +FIELD 1tp:1t => meeting/font_italic_id:-> mediafile/used_as_font_italic_in_meeting_id +FIELD 1tp:1t => meeting/font_bold_id:-> mediafile/used_as_font_bold_in_meeting_id +FIELD 1tp:1t => meeting/font_bold_italic_id:-> mediafile/used_as_font_bold_italic_in_meeting_id +FIELD 1tp:1t => meeting/font_monospace_id:-> mediafile/used_as_font_monospace_in_meeting_id +FIELD 1tp:1t => meeting/font_chyron_speaker_name_id:-> mediafile/used_as_font_chyron_speaker_name_in_meeting_id +FIELD 1tp:1t => meeting/font_projector_h1_id:-> mediafile/used_as_font_projector_h1_in_meeting_id +FIELD 1tp:1t => meeting/font_projector_h2_id:-> mediafile/used_as_font_projector_h2_in_meeting_id +FIELD 1tR:nt => meeting/committee_id:-> committee/meeting_ids +SQL 1t:1tp => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id +SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids +SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids +FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id +FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ +FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ +SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id +*** ntR:1t => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id +*** ntR:1t => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id +*** ntR:1t => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id +*** ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_list_of_speakers_in_meeting_id +*** ntR:1t => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id +*** ntR:1t => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id +*** ntR:1t => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id +*** ntR:1t => meeting/default_projector_assignment_ids:-> projector/used_as_default_projector_for_assignment_in_meeting_id +*** ntR:1t => meeting/default_projector_mediafile_ids:-> projector/used_as_default_projector_for_mediafile_in_meeting_id +*** ntR:1t => meeting/default_projector_message_ids:-> projector/used_as_default_projector_for_message_in_meeting_id +*** ntR:1t => meeting/default_projector_countdown_ids:-> projector/used_as_default_projector_for_countdown_in_meeting_id +*** ntR:1t => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id +*** ntR:1t => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id +*** ntR:1t => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id +FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id +FIELD 1tp:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id + +SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids +SQL 1t:1tR => group/default_group_for_meeting_id:-> meeting/default_group_id +SQL 1t:1tp => group/admin_group_for_meeting_id:-> meeting/admin_group_id +SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids +SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids +SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids +SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_group_ids +SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids +SQL nt:nt => group/write_chat_group_ids:-> chat_group/write_group_ids +SQL nt:nt => group/poll_ids:-> poll/entitled_group_ids +FIELD 1t:nt => group/used_as_motion_poll_default_id:-> meeting/motion_poll_default_group_ids +FIELD 1t:nt => group/used_as_assignment_poll_default_id:-> meeting/assignment_poll_default_group_ids +FIELD 1t:nt => group/used_as_topic_poll_default_id:-> meeting/topic_poll_default_group_ids +FIELD 1t:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids +FIELD 1tR:nt => group/meeting_id:-> meeting/group_ids + +FIELD 1tR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids +FIELD 1Gt:nt => personal_note/content_object_id:-> motion/personal_note_ids +FIELD 1tR:nt => personal_note/meeting_id:-> meeting/personal_note_ids + +FIELD nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids +FIELD 1tR:nt => tag/meeting_id:-> meeting/tag_ids + +FIELD 1GtR:1t,1t,1t,1tR => agenda_item/content_object_id:-> motion/agenda_item_id,motion_block/agenda_item_id,assignment/agenda_item_id,topic/agenda_item_id +FIELD 1t:nt => agenda_item/parent_id:-> agenda_item/child_ids +SQL nt:1t => agenda_item/child_ids:-> agenda_item/parent_id +SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids +SQL nt:1GtR => agenda_item/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids + +FIELD 1GtR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,mediafile/list_of_speakers_id +SQL nt:1tR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id +SQL nt:1GtR => list_of_speakers/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids + +FIELD 1tR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids +SQL nt:1t => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id + +FIELD 1tR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids +FIELD 1t:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids +FIELD 1t:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids +FIELD 1tR:nt => speaker/meeting_id:-> meeting/speaker_ids + +SQL nt:nGt => topic/attachment_ids:-> mediafile/attachment_ids +SQL 1tR:1GtR => topic/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GtR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GtR => topic/poll_ids:-> poll/content_object_id +SQL nt:1GtR => topic/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => topic/meeting_id:-> meeting/topic_ids + +FIELD 1t:nt => motion/lead_motion_id:-> motion/amendment_ids +SQL nt:1t => motion/amendment_ids:-> motion/lead_motion_id +FIELD 1t:nt => motion/sort_parent_id:-> motion/sort_child_ids +SQL nt:1t => motion/sort_child_ids:-> motion/sort_parent_id +FIELD 1t:nt => motion/origin_id:-> motion/derived_motion_ids +FIELD 1t:nt => motion/origin_meeting_id:-> meeting/forwarded_motion_ids +SQL nt:1t => motion/derived_motion_ids:-> motion/origin_id +SQL nt:nt => motion/all_origin_ids:-> motion/all_derived_motion_ids +SQL nt:nt => motion/all_derived_motion_ids:-> motion/all_origin_ids +FIELD 1tR:nt => motion/state_id:-> motion_state/motion_ids +FIELD 1t:nt => motion/recommendation_id:-> motion_state/motion_recommendation_ids +FIELD nGt:nt => motion/state_extension_reference_ids:-> motion/referenced_in_motion_state_extension_ids +SQL nt:nGt => motion/referenced_in_motion_state_extension_ids:-> motion/state_extension_reference_ids +FIELD nGt:nt => motion/recommendation_extension_reference_ids:-> motion/referenced_in_motion_recommendation_extension_ids +SQL nt:nGt => motion/referenced_in_motion_recommendation_extension_ids:-> motion/recommendation_extension_reference_ids +FIELD 1t:nt => motion/category_id:-> motion_category/motion_ids +FIELD 1t:nt => motion/block_id:-> motion_block/motion_ids +SQL nt:1tR => motion/submitter_ids:-> motion_submitter/motion_id +SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ids +FIELD 1t:nt => motion/editor_id:-> meeting_user/editor_for_motion_ids +FIELD 1t:nt => motion/working_group_speaker_id:-> meeting_user/working_group_speaker_for_motion_ids +SQL nt:1GtR => motion/poll_ids:-> poll/content_object_id +SQL nt:1Gt => motion/option_ids:-> option/content_object_id +SQL nt:1tR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id +FIELD 1t:nt => motion/statute_paragraph_id:-> motion_statute_paragraph/motion_ids +SQL nt:1tR => motion/comment_ids:-> motion_comment/motion_id +SQL 1t:1GtR => motion/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GtR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:nGt => motion/tag_ids:-> tag/tagged_ids +SQL nt:nGt => motion/attachment_ids:-> mediafile/attachment_ids +SQL nt:1GtR => motion/projection_ids:-> projection/content_object_id +SQL nt:1Gt => motion/personal_note_ids:-> personal_note/content_object_id +FIELD 1tR:nt => motion/meeting_id:-> meeting/motion_ids + +FIELD 1tR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids +FIELD 1tR:nt => motion_submitter/motion_id:-> motion/submitter_ids +FIELD 1tR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids + +FIELD 1tR:nt => motion_comment/motion_id:-> motion/comment_ids +FIELD 1tR:nt => motion_comment/section_id:-> motion_comment_section/comment_ids +FIELD 1tR:nt => motion_comment/meeting_id:-> meeting/motion_comment_ids + +SQL nt:1tR => motion_comment_section/comment_ids:-> motion_comment/section_id +SQL nt:nt => motion_comment_section/read_group_ids:-> group/read_comment_section_ids +SQL nt:nt => motion_comment_section/write_group_ids:-> group/write_comment_section_ids +FIELD 1tR:nt => motion_comment_section/meeting_id:-> meeting/motion_comment_section_ids + +FIELD 1t:nt => motion_category/parent_id:-> motion_category/child_ids +SQL nt:1t => motion_category/child_ids:-> motion_category/parent_id +SQL nt:1t => motion_category/motion_ids:-> motion/category_id +FIELD 1tR:nt => motion_category/meeting_id:-> meeting/motion_category_ids + +SQL nt:1t => motion_block/motion_ids:-> motion/block_id +SQL 1t:1GtR => motion_block/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GtR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GtR => motion_block/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => motion_block/meeting_id:-> meeting/motion_block_ids + +FIELD 1tR:nt => motion_change_recommendation/motion_id:-> motion/change_recommendation_ids +FIELD 1tR:nt => motion_change_recommendation/meeting_id:-> meeting/motion_change_recommendation_ids + +FIELD 1t:nt => motion_state/submitter_withdraw_state_id:-> motion_state/submitter_withdraw_back_ids +SQL nt:1t => motion_state/submitter_withdraw_back_ids:-> motion_state/submitter_withdraw_state_id +SQL nt:nt => motion_state/next_state_ids:-> motion_state/previous_state_ids +SQL nt:nt => motion_state/previous_state_ids:-> motion_state/next_state_ids +SQL nt:1tR => motion_state/motion_ids:-> motion/state_id +SQL nt:1t => motion_state/motion_recommendation_ids:-> motion/recommendation_id +FIELD 1tR:nt => motion_state/workflow_id:-> motion_workflow/state_ids +SQL 1t:1tR => motion_state/first_state_of_workflow_id:-> motion_workflow/first_state_id +FIELD 1tR:nt => motion_state/meeting_id:-> meeting/motion_state_ids + +SQL nt:1tR => motion_workflow/state_ids:-> motion_state/workflow_id +FIELD 1tR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id +SQL 1t:1tR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id +SQL 1t:1tR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id +SQL 1t:1tR => motion_workflow/default_statute_amendment_workflow_meeting_id:-> meeting/motions_default_statute_amendment_workflow_id +FIELD 1tR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids + +SQL nt:1t => motion_statute_paragraph/motion_ids:-> motion/statute_paragraph_id +FIELD 1tR:nt => motion_statute_paragraph/meeting_id:-> meeting/motion_statute_paragraph_ids + +FIELD 1GtR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids +SQL nt:1t => poll/option_ids:-> option/poll_id +FIELD 1r: => poll/global_option_id:-> option/ +SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids +SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids +SQL nt:1GtR => poll/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => poll/meeting_id:-> meeting/poll_ids + +FIELD 1t:nt => option/poll_id:-> poll/option_ids +SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id +SQL nt:1tR => option/vote_ids:-> vote/option_id +FIELD 1Gt:nt,nt,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id +FIELD 1tR:nt => option/meeting_id:-> meeting/option_ids + +FIELD 1tR:nt => vote/option_id:-> option/vote_ids +FIELD 1t:nt => vote/user_id:-> user/vote_ids +FIELD 1t:nt => vote/delegated_user_id:-> user/delegated_vote_ids +FIELD 1tR:nt => vote/meeting_id:-> meeting/vote_ids + +SQL nt:1tR => assignment/candidate_ids:-> assignment_candidate/assignment_id +SQL nt:1GtR => assignment/poll_ids:-> poll/content_object_id +SQL 1t:1GtR => assignment/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GtR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids +SQL nt:nGt => assignment/attachment_ids:-> mediafile/attachment_ids +SQL nt:1GtR => assignment/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => assignment/meeting_id:-> meeting/assignment_ids + +FIELD 1tR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids +FIELD 1t:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids +FIELD 1tR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids + +SQL nt:1tR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id +FIELD 1tR:nt => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids +SQL 1tR:1Gt => poll_candidate_list/option_id:-> option/content_object_id + +FIELD 1tR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids +FIELD 1t:nt => poll_candidate/user_id:-> user/poll_candidate_ids +FIELD 1tR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids + +SQL nt:nt => mediafile/inherited_access_group_ids:-> group/mediafile_inherited_access_group_ids +SQL nt:nt => mediafile/access_group_ids:-> group/mediafile_access_group_ids +FIELD 1t:nt => mediafile/parent_id:-> mediafile/child_ids +SQL nt:1t => mediafile/child_ids:-> mediafile/parent_id +SQL 1t:1GtR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GtR => mediafile/projection_ids:-> projection/content_object_id +FIELD nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids +FIELD 1GtR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids +SQL 1t:1tp => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id +SQL 1t:1tp => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id +SQL 1t:1tp => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id +SQL 1t:1tp => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id +SQL 1t:1tp => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id +SQL 1t:1tp => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id +SQL 1t:1tp => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id +SQL 1t:1tp => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id +SQL 1t:1tp => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id +SQL 1t:1tp => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id +SQL 1t:1tp => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id +SQL 1t:1tp => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id +SQL 1t:1tp => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id +SQL 1t:1tp => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id +SQL 1t:1tp => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id +SQL 1t:1tp => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id + +SQL nt:1t => projector/current_projection_ids:-> projection/current_projector_id +SQL nt:1t => projector/preview_projection_ids:-> projection/preview_projector_id +SQL nt:1t => projector/history_projection_ids:-> projection/history_projector_id +SQL 1t:1tR => projector/used_as_reference_projector_meeting_id:-> meeting/reference_projector_id +*** 1t:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids +*** 1t:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids +*** 1t:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids +*** 1t:ntR => projector/used_as_default_projector_for_current_list_of_speakers_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids +*** 1t:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids +*** 1t:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids +*** 1t:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids +*** 1t:ntR => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/default_projector_assignment_ids +*** 1t:ntR => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/default_projector_mediafile_ids +*** 1t:ntR => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/default_projector_message_ids +*** 1t:ntR => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/default_projector_countdown_ids +*** 1t:ntR => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/default_projector_assignment_poll_ids +*** 1t:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/default_projector_motion_poll_ids +*** 1t:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids +FIELD 1tR:nt => projector/meeting_id:-> meeting/projector_ids + +FIELD 1t:nt => projection/current_projector_id:-> projector/current_projection_ids +FIELD 1t:nt => projection/preview_projector_id:-> projector/preview_projection_ids +FIELD 1t:nt => projection/history_projector_id:-> projector/history_projection_ids +FIELD 1GtR:nt,nt,nt,nt,nt,nt,nt,nt,nt,nt,nt => projection/content_object_id:-> meeting/projection_ids,motion/projection_ids,mediafile/projection_ids,list_of_speakers/projection_ids,motion_block/projection_ids,assignment/projection_ids,agenda_item/projection_ids,topic/projection_ids,poll/projection_ids,projector_message/projection_ids,projector_countdown/projection_ids +FIELD 1tR:nt => projection/meeting_id:-> meeting/all_projection_ids + +SQL nt:1GtR => projector_message/projection_ids:-> projection/content_object_id +FIELD 1tR:nt => projector_message/meeting_id:-> meeting/projector_message_ids + +SQL nt:1GtR => projector_countdown/projection_ids:-> projection/content_object_id +SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id +SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id +FIELD 1tR:nt => projector_countdown/meeting_id:-> meeting/projector_countdown_ids + +SQL nt:1tR => chat_group/chat_message_ids:-> chat_message/chat_group_id +SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids +SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids +FIELD 1tR:nt => chat_group/meeting_id:-> meeting/chat_group_ids + +FIELD 1tR:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids +FIELD 1tR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids +FIELD 1tR:nt => chat_message/meeting_id:-> meeting/chat_message_ids + +*/ + +/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, primary */ \ No newline at end of file From 090858f3e36700d44dc4499d5711799977fd7d77 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 8 Feb 2024 12:13:01 +0100 Subject: [PATCH 002/142] intermediate commit --- cli/generate_sql_schema.md | 7 +++ cli/generate_sql_schema.py | 106 +++++++++++------------------------ cli/libs/helper_get_names.py | 63 +++++++++++++++++++++ 3 files changed, 102 insertions(+), 74 deletions(-) create mode 100644 cli/generate_sql_schema.md create mode 100644 cli/libs/helper_get_names.py diff --git a/cli/generate_sql_schema.md b/cli/generate_sql_schema.md new file mode 100644 index 00000000..7e5e8ab3 --- /dev/null +++ b/cli/generate_sql_schema.md @@ -0,0 +1,7 @@ +# Readme for relational sql schema + +## Naming conventions for intermediate files + +### relation-list versus relation-list + +### generic-relation-list versus relation-list diff --git a/cli/generate_sql_schema.py b/cli/generate_sql_schema.py index e6ec893c..d45a42de 100644 --- a/cli/generate_sql_schema.py +++ b/cli/generate_sql_schema.py @@ -13,33 +13,13 @@ import requests import yaml +from libs.helper_get_names import HelperGetNames, TableFieldType SOURCE = "global/meta/models.yml" DESTINATION = "global/meta/schema_relational.sql" MODELS: Dict[str, Dict[str, Any]] = {} -class TableFieldType: - def __init__( - self, - table: str, - column: str, - field_def: Optional[Dict[str, Any]], - ref_column: str = "id", - ): - self.table = table - self.column = column - self.field_def: Dict[str, Any] = field_def or {} - self.ref_column = ref_column - - @property - def fqid(self) -> str: - if self.table: - return f"{self.table}/{self.column}" - else: - return "-" - - class SchemaZoneTexts(TypedDict, total=False): """TypedDict definition for generation of different sql-code parts""" @@ -317,7 +297,7 @@ def get_sql_for_relation_1_1( table_letter = Helper.get_table_letter(table_name) letters = [table_letter] foreign_letter = Helper.get_table_letter(foreign_table, letters) - foreign_table = Helper.get_table_name(foreign_table) + foreign_table = HelperGetNames.get_table_name(foreign_table) return f"(select {foreign_letter}.{ref_column} from {foreign_table} {foreign_letter} where {foreign_letter}.{foreign_column} = {table_letter}.{ref_column}) as {fname},\n" @classmethod @@ -326,7 +306,7 @@ def get_relation_list_type( ) -> SchemaZoneTexts: """ ("relation-list", "relation"): False, - ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.fqid + ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.collectionfield ("relation-list", "generic-relation"): False, ("relation-list", "generic-relation-list"): False, ("relation-list", None): "decide_sql", // True or Exception @@ -367,17 +347,17 @@ def get_relation_list_type( """ Example: committee.forward_to_committee_ids to committee.receive_forwardings_from_committee_ids""" own_ref_column = own_table_field.ref_column foreign_table_ref_column = fname[:-1] - foreign_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + foreign_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) foreign_table_column = foreign_table_field.column[:-1] else: own_ref_column = own_table_field.ref_column foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" - foreign_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + foreign_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) foreign_table_column = f"{own_table_field.table}_{own_table_field.ref_column}" elif type_ == "generic-relation-list": own_ref_column = own_table_field.ref_column foreign_table_ref_column = foreign_table_field.column[:-1] - foreign_table_name = Helper.get_gm_table_name(foreign_table_field) + foreign_table_name = HelperGetNames.get_gm_table_name(foreign_table_field) foreign_table_column = f"{foreign_table_column[:-1]}_{table_name}_id" elif type_ == "relation" or foreign_table_field_ref_id: own_ref_column = own_table_field.ref_column @@ -411,7 +391,7 @@ def get_sql_for_relation_n_1( table_letter = Helper.get_table_letter(table_name) letters = [table_letter] foreign_letter = Helper.get_table_letter(foreign_table_name, letters) - foreign_table_name = Helper.get_table_name(foreign_table_name) + foreign_table_name = HelperGetNames.get_table_name(foreign_table_name) if foreign_table_column: return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter} where {foreign_letter}.{foreign_table_column} = {table_letter}.{own_ref_column}) as {fname},\n" else: @@ -440,7 +420,7 @@ def get_generic_relation_type( for foreign_table_field in foreign_table_fields ): raise Exception( - f"Error in generation for fqid '{own_table_field.fqid}'" + f"Error in generation for collectionfield '{own_table_field.collectionfield}'" ) text.update(cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"])) initially_deferred = any( @@ -488,7 +468,7 @@ def get_generic_relation_list_type( for foreign_table_field in foreign_table_fields ): raise Exception( - f"Error in generation for fqid '{own_table_field.fqid}'" + f"Error in generation for collectfield '{own_table_field.collectionfield}'" ) # create gm-intermediate table gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists(own_table_field, foreign_table_fields) @@ -609,32 +589,10 @@ class Helper: """ ) - @staticmethod - def get_table_name(table_name: str) -> str: - return table_name + "T" - - @staticmethod - def get_view_name(table_name: str) -> str: - if table_name in ("group", "user"): - return table_name + "_" - return table_name - - @staticmethod - def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: - """ table name n:m-relations intermediate table""" - if (own_str := f"{own.table}_{own.column}") < (foreign_str := f"{foreign.table}_{foreign.column}"): - return f"nm_{own_str}_{foreign.table}" - else: - return f"nm_{foreign_str}_{own.table}" - - @staticmethod - def get_gm_table_name(table_field: TableFieldType) -> str: - """ table name generic-list:m-relations intermediate table""" - return f"gm_{table_field.table}_{table_field.column}" @staticmethod def get_table_letter(table_name: str, letters: List[str] = []) -> str: - letter = Helper.get_table_name(table_name)[0] + letter = HelperGetNames.get_table_name(table_name)[0] count = -1 start_letter = letter while True: @@ -652,7 +610,7 @@ def get_table_letter(table_name: str, letters: List[str] = []) -> str: @staticmethod def get_table_head(table_name: str) -> str: - return f"\nCREATE TABLE IF NOT EXISTS {Helper.get_table_name(table_name)} (\n" + return f"\nCREATE TABLE IF NOT EXISTS {HelperGetNames.get_table_name(table_name)} (\n" @staticmethod def get_table_body_end(code: str) -> str: @@ -662,12 +620,12 @@ def get_table_body_end(code: str) -> str: @staticmethod def get_view_head(table_name: str) -> str: - return f"\nCREATE OR REPLACE VIEW {Helper.get_view_name(table_name)} AS SELECT *,\n" + return f"\nCREATE OR REPLACE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" @staticmethod def get_view_body_end(table_name: str, code: str) -> str: code = code[:-2] + "\n" # last attribute line without ",", but with "\n" - code += f"FROM {Helper.get_table_name(table_name)} {Helper.get_table_letter(table_name)};\n\n" + code += f"FROM {HelperGetNames.get_table_name(table_name)} {Helper.get_table_letter(table_name)};\n\n" return code @staticmethod @@ -722,8 +680,8 @@ def get_foreign_key_table_constraint_as_alter_table( own_columns = "(" + ", ".join(own_columns) + ")" if isinstance(fk_columns, list): fk_columns = "(" + ", ".join(fk_columns) + ")" - own_table = Helper.get_table_name(table_name) - foreign_table = Helper.get_table_name(foreign_table) + own_table = HelperGetNames.get_table_name(table_name) + foreign_table = HelperGetNames.get_table_name(foreign_table) result = FOREIGN_KEY_TABLE_CONSTRAINT_TEMPLATE.substitute( { "own_table": own_table, @@ -769,22 +727,22 @@ def get_foreign_key_table_column( @staticmethod def get_nm_table_for_n_m_relation_lists(own_table_field: TableFieldType, foreign_table_field: TableFieldType) -> Tuple[str, str]: - nm_table_name = Helper.get_nm_table_name(own_table_field, foreign_table_field) + nm_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) field1 = Helper.get_field_in_n_m_relation_list(own_table_field, foreign_table_field.table) field2 = Helper.get_field_in_n_m_relation_list(foreign_table_field, own_table_field.table) text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute({ - "table_name": Helper.get_table_name(nm_table_name), + "table_name": HelperGetNames.get_table_name(nm_table_name), "field1": field1, - "table1": Helper.get_table_name(own_table_field.table), + "table1": HelperGetNames.get_table_name(own_table_field.table), "field2": field2, - "table2": Helper.get_table_name(foreign_table_field.table), + "table2": HelperGetNames.get_table_name(foreign_table_field.table), "list_of_keys": ", ".join([field1, field2]), }) return nm_table_name, text @staticmethod def get_gm_table_for_gm_nm_relation_lists(own_table_field: TableFieldType, foreign_table_fields: List[TableFieldType]) -> Tuple[str, str]: - gm_table_name = Helper.get_gm_table_name(own_table_field) + gm_table_name = HelperGetNames.get_gm_table_name(own_table_field) joined_table_names = "('" + "', '".join([foreign_table_field.table for foreign_table_field in foreign_table_fields]) + "')" foreign_table_ref_lines = [] subst_dict = { @@ -795,8 +753,8 @@ def get_gm_table_for_gm_nm_relation_lists(own_table_field: TableFieldType, forei foreign_table_ref_lines.append(Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict)) text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute({ - "table_name": Helper.get_table_name(gm_table_name), - "own_table_name": Helper.get_table_name(own_table_field.table), + "table_name": HelperGetNames.get_table_name(gm_table_name), + "own_table_name": HelperGetNames.get_table_name(own_table_field.table), "own_table_name_with_ref_column": f"{own_table_field.table}_{own_table_field.ref_column}", "own_table_ref_column": own_table_field.ref_column, "own_table_column": own_table_field.column[:-1], @@ -854,7 +812,7 @@ def get_initials( if comment := fdata.get("description"): text[ "alter_table" - ] = f"comment on column {Helper.get_table_name(table_name)}.{fname} is '{comment}';\n" + ] = f"comment on column {HelperGetNames.get_table_name(table_name)}.{fname} is '{comment}';\n" return subst, text @staticmethod @@ -921,12 +879,12 @@ def check_relation_definitions( own_c, tmp_error = Helper.get_cardinality(own.field_def) error = error or tmp_error foreigns_c = [] - foreign_fqids = [] + foreign_collectionfields = [] for foreign in foreigns: foreign_c, tmp_error = Helper.get_cardinality(foreign.field_def) foreigns_c.append(foreign_c) error = error or tmp_error - foreign_fqids.append(foreign.fqid) + foreign_collectionfields.append(foreign.collectionfield) # if table_field["type"] == "relation": # if foreign_field and foreign_field.get("type") == "relation": @@ -942,7 +900,7 @@ def check_relation_definitions( # error = True if error: text = "*** " - text += f"{own_c}:{','.join(foreigns_c)} => {own.fqid}:-> {','.join(foreign_fqids)}\n" + text += f"{own_c}:{','.join(foreigns_c)} => {own.collectionfield}:-> {','.join(foreign_collectionfields)}\n" return text, error @staticmethod @@ -983,7 +941,7 @@ def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) ] if result == "not implemented": raise Exception( - f"Type combination not implemented: {own_type}:{foreign_type} on field {own.fqid}" + f"Type combination not implemented: {own_type}:{foreign_type} on field {own.collectionfield}" ) elif result == "decide_primary_side": if own.field_def.get("required", False) == foreign.field_def.get( @@ -997,7 +955,7 @@ def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) ): if own.field_def.get("primary", False) == foreign.field_def.get("primary", False): raise Exception( - f"Type combination undecidable: {own_type}:{foreign_type} on field {own.fqid}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" + f"Type combination undecidable: {own_type}:{foreign_type} on field {own.collectionfield}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" ) else: return own.field_def.get("primary", False) @@ -1008,12 +966,12 @@ def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) else: return own.field_def.get("required", False) elif result == "decide_alphabetical": - return foreign.fqid == "-" or own.fqid < foreign.fqid + return foreign.collectionfield == "-" or own.collectionfield < foreign.collectionfield elif result == "decide_sql": if own.field_def.get("sql") or own.field_def.get("reference"): return True else: - raise Exception(f"Missing sql-or to-attribute for field {own.fqid}") + raise Exception(f"Missing sql-or to-attribute for field {own.collectionfield}") return cast(bool, result) @staticmethod @@ -1087,8 +1045,8 @@ def get_definitions_from_foreign_list( ModelsHelper.get_definitions_from_foreign(table + fname, None) ) elif isinstance(to, list): - for fqid in to: - results.append(ModelsHelper.get_definitions_from_foreign(fqid, None)) + for collectionfield in to: + results.append(ModelsHelper.get_definitions_from_foreign(collectionfield, None)) elif reference: for ref in reference: results.append(ModelsHelper.get_definitions_from_foreign(None, ref)) diff --git a/cli/libs/helper_get_names.py b/cli/libs/helper_get_names.py new file mode 100644 index 00000000..eb273df0 --- /dev/null +++ b/cli/libs/helper_get_names.py @@ -0,0 +1,63 @@ +from typing import Any, Dict, Optional + +KEYSEPARATOR = "/" + +class TableFieldType: + def __init__( + self, + table: str, + column: str, + field_def: Optional[Dict[str, Any]], + ref_column: str = "id", + ): + self.table = table + self.column = column + self.field_def: Dict[str, Any] = field_def or {} + self.ref_column = ref_column + + @property + def collectionfield(self) -> str: + if self.table: + return f"{self.table}{KEYSEPARATOR}{self.column}" + else: + return "-" + + +class HelperGetNames: + MAX_LEN = 63 + + def max_length(func) -> str: + def wrapper(*args, **kwargs): + name = func(*args, **kwargs) + assert len(name) <= HelperGetNames.MAX_LEN, f"Generated name '{name}' to long in function {func}!" + return name + return wrapper + + @staticmethod + @max_length + def get_table_name(table_name: str) -> str: + return table_name + "T" + + @staticmethod + @max_length + def get_view_name(table_name: str) -> str: + if table_name in ("group", "user"): + return table_name + "_" + return table_name + + @staticmethod + @max_length + def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: + """ table name n:m-relations intermediate table""" + if (own_str := f"{own.table}_{own.column}") < (foreign_str := f"{foreign.table}_{foreign.column}"): + return f"nm_{own_str}_{foreign.table}" + else: + return f"nm_{foreign_str}_{own.table}" + + @staticmethod + @max_length + def get_gm_table_name(table_field: TableFieldType) -> str: + """ table name generic-list:m-relations intermediate table""" + return f"gm_{table_field.table}_{table_field.column}" + + From 7346c76b546657cc3bf5a4bcfd127bc3d57d2e19 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 8 Feb 2024 17:40:36 +0100 Subject: [PATCH 003/142] files moved to new destinations --- {cli => dev/doc}/generate_sql_schema.md | 0 dev/docker-compose.yml | 2 + dev/requirements.txt | 3 + {cli => dev/sql}/example_transactional.sql | 0 .../sql/schema_relational.sql | 2 +- {cli => dev/src}/generate_sql_schema.py | 370 +++++++++++------- {cli/libs => dev/src}/helper_get_names.py | 22 +- 7 files changed, 243 insertions(+), 156 deletions(-) rename {cli => dev/doc}/generate_sql_schema.md (100%) rename {cli => dev/sql}/example_transactional.sql (100%) rename schema_relational.sql => dev/sql/schema_relational.sql (99%) rename {cli => dev/src}/generate_sql_schema.py (81%) rename {cli/libs => dev/src}/helper_get_names.py (71%) diff --git a/cli/generate_sql_schema.md b/dev/doc/generate_sql_schema.md similarity index 100% rename from cli/generate_sql_schema.md rename to dev/doc/generate_sql_schema.md diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index dc885f8f..04e6f91e 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -3,6 +3,8 @@ services: models: build: . user: $USER_ID:$GROUP_ID + ports: + - 5678:5678 volumes: - ..:/app depends_on: diff --git a/dev/requirements.txt b/dev/requirements.txt index 5dd8ac34..b660bdb6 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -7,7 +7,10 @@ pytest==8.0.0 pyupgrade==3.15.0 pyyaml==6.0.1 simplejson==3.19.2 +debugpy==1.8.0 +requests==2.31.0 # typing types-PyYAML==6.0.12.12 types-simplejson==3.19.0.2 +types-requests==2.31.0.20240125 diff --git a/cli/example_transactional.sql b/dev/sql/example_transactional.sql similarity index 100% rename from cli/example_transactional.sql rename to dev/sql/example_transactional.sql diff --git a/schema_relational.sql b/dev/sql/schema_relational.sql similarity index 99% rename from schema_relational.sql rename to dev/sql/schema_relational.sql index 11b1ea5d..dedbb7e7 100644 --- a/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -2,7 +2,7 @@ -- schema.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'faa2401887834e2fdfbb875185412256' +-- MODELS_YML_CHECKSUM = 'c942b45a22fcdb3b4d25726f48a411dc' -- Type definitions DO $$ BEGIN diff --git a/cli/generate_sql_schema.py b/dev/src/generate_sql_schema.py similarity index 81% rename from cli/generate_sql_schema.py rename to dev/src/generate_sql_schema.py index d45a42de..4db1bbb4 100644 --- a/cli/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -4,20 +4,21 @@ import string import sys from collections import defaultdict +from collections.abc import Callable from decimal import Decimal from enum import Enum +from pathlib import Path from string import Formatter from textwrap import dedent -from typing import (Any, Callable, Dict, List, Optional, Tuple, TypedDict, - Union, cast) +from typing import Any, TypedDict, cast import requests import yaml -from libs.helper_get_names import HelperGetNames, TableFieldType +from helper_get_names import HelperGetNames, TableFieldType -SOURCE = "global/meta/models.yml" -DESTINATION = "global/meta/schema_relational.sql" -MODELS: Dict[str, Dict[str, Any]] = {} +SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() +DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() +MODELS: dict[str, dict[str, Any]] = {} class SchemaZoneTexts(TypedDict, total=False): @@ -34,7 +35,7 @@ class SchemaZoneTexts(TypedDict, total=False): class ToDict(TypedDict): """Defines the dict keys for the to-Attribute of generic relations in field definitions""" - collections: List[str] + collections: list[str] field: str @@ -61,10 +62,13 @@ class SubstDict(TypedDict, total=False): class GenerateCodeBlocks: """Main work is done here by recursing the models and their fields and determine the method to use""" - intermediate_tables: Dict[str, str] = {} # Key=Name, data: collected content of table + + intermediate_tables: dict[str, str] = ( + {} + ) # Key=Name, data: collected content of table @classmethod - def generate_the_code(cls) -> Tuple[str, str, str, str, str, List[str], str]: + def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -73,30 +77,28 @@ def generate_the_code(cls) -> Tuple[str, str, str, str, str, List[str], str]: alter_table_final_code: Changes on tables defining relations after, which should appear after all table/views definition to be sequence independant final_info_code: Detailed info about all relation fields.Types: relation, relation-list, generic-relation and generic-relation-list missing_handled_atributes: List of unhandled attributes. handled one's are to be set manually. - im_table_code: Code for intermediate tables. + im_table_code: Code for intermediate tables. n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field """ - handled_attributes = set( - ( - "required", - "maxLength", - "minLength", - "default", - "type", - "restriction_mode", - "minimum", - "calculated", - "description", - "read_only", - "enum", - "items", - "to", # will be used for creating view-fields, but also replacement for fk-reference to id - # "on_delete", # must have other name then the key-value-store one - # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? - # "unique", # still to design - ) - ) + handled_attributes = { + "required", + "maxLength", + "minLength", + "default", + "type", + "restriction_mode", + "minimum", + "calculated", + "description", + "read_only", + "enum", + "items", + "to", # will be used for creating view-fields, but also replacement for fk-reference to id + # "on_delete", # must have other name then the key-value-store one + # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? + # "unique", # still to design + } pre_code: str = "" table_name_code: str = "" view_name_code: str = "" @@ -157,7 +159,6 @@ def generate_the_code(cls) -> Tuple[str, str, str, str, str, List[str], str]: for im_table in cls.intermediate_tables.values(): im_table_code += im_table - return ( pre_code, table_name_code, @@ -170,8 +171,8 @@ def generate_the_code(cls) -> Tuple[str, str, str, str, str, List[str], str]: @classmethod def get_method( - cls, fname: str, fdata: Dict[str, Any] - ) -> Tuple[Union[str, Callable[..., SchemaZoneTexts]], str]: + cls, fname: str, fdata: dict[str, Any] + ) -> tuple[str | Callable[..., SchemaZoneTexts], str]: if fdata.get("calculated"): return ( f" {fname} type:{fdata.get('type')} is marked as a calculated field\n", @@ -191,7 +192,7 @@ def get_method( @classmethod def get_schema_simple_types( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts subst, text = Helper.get_initials(table_name, fname, type_, fdata) @@ -208,7 +209,7 @@ def get_schema_simple_types( @classmethod def get_schema_color( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts subst, text = Helper.get_initials(table_name, fname, type_, fdata) @@ -221,7 +222,7 @@ def get_schema_color( @classmethod def get_schema_primary_key( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts subst, text = Helper.get_initials(table_name, fname, type_, fdata) @@ -231,7 +232,7 @@ def get_schema_primary_key( @classmethod def get_relation_type( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) @@ -252,12 +253,14 @@ def get_relation_type( initially_deferred = ModelsHelper.is_fk_initially_deferred( table_name, foreign_table_field.table ) - text["alter_table_final"] = Helper.get_foreign_key_table_constraint_as_alter_table( - table_name, - foreign_table_field.table, - fname, - foreign_table_field.ref_column, - initially_deferred, + text["alter_table_final"] = ( + Helper.get_foreign_key_table_constraint_as_alter_table( + table_name, + foreign_table_field.table, + fname, + foreign_table_field.ref_column, + initially_deferred, + ) ) final_info = "FIELD " + final_info elif result is False: @@ -302,14 +305,14 @@ def get_sql_for_relation_1_1( @classmethod def get_relation_list_type( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: """ - ("relation-list", "relation"): False, - ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.collectionfield - ("relation-list", "generic-relation"): False, - ("relation-list", "generic-relation-list"): False, - ("relation-list", None): "decide_sql", // True or Exception + ("relation-list", "relation"): False, + ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.collectionfield + ("relation-list", "generic-relation"): False, + ("relation-list", "generic-relation-list"): False, + ("relation-list", None): "decide_sql", // True or Exception """ text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) @@ -326,46 +329,66 @@ def get_relation_list_type( own_table_field, foreign_table_field ): if foreign_table_field.field_def.get("type") == "relation-list": - nm_table_name, value = Helper.get_nm_table_for_n_m_relation_lists(own_table_field, foreign_table_field) + nm_table_name, value = Helper.get_nm_table_for_n_m_relation_lists( + own_table_field, foreign_table_field + ) if nm_table_name not in cls.intermediate_tables: cls.intermediate_tables[nm_table_name] = value else: - raise Exception(f"Tried to create im_table '{nm_table_name}' twice") + raise Exception( + f"Tried to create im_table '{nm_table_name}' twice" + ) if sql := fdata.get("sql", ""): text["view"] = sql + ",\n" else: foreign_table_column = cast(str, foreign_table_field.column) foreign_table_field_ref_id = cast(str, foreign_table_field.ref_column) if foreign_table_column or foreign_table_field_ref_id: - if (type_ := foreign_table_field.field_def.get("type")) == "generic-relation": + if ( + type_ := foreign_table_field.field_def.get("type") + ) == "generic-relation": own_ref_column = own_table_field.ref_column - foreign_table_column += f"_{table_name}_{foreign_table_field.ref_column}" + foreign_table_column += ( + f"_{table_name}_{foreign_table_field.ref_column}" + ) foreign_table_name = foreign_table_field.table foreign_table_ref_column = foreign_table_field.column elif type_ == "relation-list": if own_table_field.table == foreign_table_field.table: - """ Example: committee.forward_to_committee_ids to committee.receive_forwardings_from_committee_ids""" + """Example: committee.forward_to_committee_ids to committee.receive_forwardings_from_committee_ids""" own_ref_column = own_table_field.ref_column foreign_table_ref_column = fname[:-1] - foreign_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) + foreign_table_name = HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ) foreign_table_column = foreign_table_field.column[:-1] else: own_ref_column = own_table_field.ref_column foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" - foreign_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) - foreign_table_column = f"{own_table_field.table}_{own_table_field.ref_column}" + foreign_table_name = HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ) + foreign_table_column = ( + f"{own_table_field.table}_{own_table_field.ref_column}" + ) elif type_ == "generic-relation-list": own_ref_column = own_table_field.ref_column foreign_table_ref_column = foreign_table_field.column[:-1] - foreign_table_name = HelperGetNames.get_gm_table_name(foreign_table_field) - foreign_table_column = f"{foreign_table_column[:-1]}_{table_name}_id" + foreign_table_name = HelperGetNames.get_gm_table_name( + foreign_table_field + ) + foreign_table_column = ( + f"{foreign_table_column[:-1]}_{table_name}_id" + ) elif type_ == "relation" or foreign_table_field_ref_id: own_ref_column = own_table_field.ref_column foreign_table_ref_column = foreign_table_field.ref_column foreign_table_name = foreign_table_field.table foreign_table_column = foreign_table_field.column else: - raise Exception(f"Still not implemented for foreign_table type '{type_}' in False case") + raise Exception( + f"Still not implemented for foreign_table type '{type_}' in False case" + ) text["view"] = cls.get_sql_for_relation_n_1( table_name, fname, @@ -399,14 +422,14 @@ def get_sql_for_relation_n_1( @classmethod def get_generic_relation_type( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts = defaultdict(str) own_table_field = TableFieldType(table_name, fname, fdata) - foreign_table_fields: List[ - TableFieldType - ] = ModelsHelper.get_definitions_from_foreign_list( - table_name, fname, fdata.get("to"), fdata.get("reference") + foreign_table_fields: list[TableFieldType] = ( + ModelsHelper.get_definitions_from_foreign_list( + table_name, fname, fdata.get("to"), fdata.get("reference") + ) ) error = False @@ -416,46 +439,58 @@ def get_generic_relation_type( if not error: if not all( - Helper.generate_field_view_or_nothing(own_table_field, foreign_table_field) + Helper.generate_field_view_or_nothing( + own_table_field, foreign_table_field + ) for foreign_table_field in foreign_table_fields ): raise Exception( f"Error in generation for collectionfield '{own_table_field.collectionfield}'" ) - text.update(cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"])) + text.update( + cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"]) + ) initially_deferred = any( ModelsHelper.is_fk_initially_deferred( table_name, foreign_table_field.table ) for foreign_table_field in foreign_table_fields ) - foreign_tables: List[str] = [] + foreign_tables: list[str] = [] for foreign_table_field in foreign_table_fields: generic_plain_field_name = f"{own_table_field.column}_{foreign_table_field.table}_{foreign_table_field.ref_column}" foreign_tables.append(foreign_table_field.table) - text["table"] += Helper.get_generic_combined_fields(generic_plain_field_name, own_table_field.column, foreign_table_field.table) - text["alter_table_final"] += Helper.get_foreign_key_table_constraint_as_alter_table( + text["table"] += Helper.get_generic_combined_fields( + generic_plain_field_name, + own_table_field.column, + foreign_table_field.table, + ) + text[ + "alter_table_final" + ] += Helper.get_foreign_key_table_constraint_as_alter_table( own_table_field.table, foreign_table_field.table, generic_plain_field_name, foreign_table_field.ref_column, initially_deferred, ) - text["table"] += Helper.get_generic_field_constraint(own_table_field.column, foreign_tables) + text["table"] += Helper.get_generic_field_constraint( + own_table_field.column, foreign_tables + ) final_info = "FIELD " + final_info text["final_info"] = final_info return text @classmethod def get_generic_relation_list_type( - cls, table_name: str, fname: str, fdata: Dict[str, Any], type_: str + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) - foreign_table_fields: List[ - TableFieldType - ] = ModelsHelper.get_definitions_from_foreign_list( - table_name, fname, fdata.get("to"), fdata.get("reference") + foreign_table_fields: list[TableFieldType] = ( + ModelsHelper.get_definitions_from_foreign_list( + table_name, fname, fdata.get("to"), fdata.get("reference") + ) ) error = False final_info, error = Helper.check_relation_definitions( @@ -464,14 +499,19 @@ def get_generic_relation_list_type( if not error: if not all( - Helper.generate_field_view_or_nothing(own_table_field, foreign_table_field) and foreign_table_field.field_def["type"] == "relation-list" + Helper.generate_field_view_or_nothing( + own_table_field, foreign_table_field + ) + and foreign_table_field.field_def["type"] == "relation-list" for foreign_table_field in foreign_table_fields ): raise Exception( f"Error in generation for collectfield '{own_table_field.collectionfield}'" ) # create gm-intermediate table - gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists(own_table_field, foreign_table_fields) + gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( + own_table_field, foreign_table_fields + ) if gm_foreign_table not in cls.intermediate_tables: cls.intermediate_tables[gm_foreign_table] = value else: @@ -484,7 +524,7 @@ def get_generic_relation_list_type( own_table_field.ref_column, gm_foreign_table, f"{own_table_field.table}_{own_table_field.ref_column}", - own_table_field.ref_column + own_table_field.ref_column, ) # # add foreign key constraints for nm-relation-tables @@ -558,11 +598,10 @@ class Helper: """ ) ) - GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( + GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( " ${own_table_column}_${foreign_table_name}_id integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_table_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}T(id)," ) - RELATION_LIST_AGENDA = dedent( """ /* Relation-list infos @@ -589,9 +628,8 @@ class Helper: """ ) - @staticmethod - def get_table_letter(table_name: str, letters: List[str] = []) -> str: + def get_table_letter(table_name: str, letters: list[str] = []) -> str: letter = HelperGetNames.get_table_name(table_name)[0] count = -1 start_letter = letter @@ -649,7 +687,7 @@ def get_enum_type_name( return f"enum_{fname}" @staticmethod - def get_enum_type_definition(table_name: str, fname: str, enum_: List[Any]) -> str: + def get_enum_type_definition(table_name: str, fname: str, enum_: list[Any]) -> str: # enums per type are always strings in postgres enumeration = ", ".join([f"'{str(item)}'" for item in enum_]) subst = { @@ -662,8 +700,8 @@ def get_enum_type_definition(table_name: str, fname: str, enum_: List[Any]) -> s def get_foreign_key_table_constraint_as_alter_table( table_name: str, foreign_table: str, - own_columns: Union[List[str], str], - fk_columns: Union[List[str], str], + own_columns: list[str] | str, + fk_columns: list[str] | str, initially_deferred: bool = False, delete_action: str = "", update_action: str = "", @@ -707,8 +745,8 @@ def get_on_action_mode(action: str, delete: bool) -> str: @staticmethod def get_foreign_key_table_column( - to: Optional[str], reference: Optional[str] - ) -> Tuple[str, str]: + to: str | None, reference: str | None + ) -> tuple[str, str]: if reference: result = Helper.ref_compiled.search(reference) if result is None: @@ -726,45 +764,72 @@ def get_foreign_key_table_column( raise Exception("Relation field without reference or to") @staticmethod - def get_nm_table_for_n_m_relation_lists(own_table_field: TableFieldType, foreign_table_field: TableFieldType) -> Tuple[str, str]: - nm_table_name = HelperGetNames.get_nm_table_name(own_table_field, foreign_table_field) - field1 = Helper.get_field_in_n_m_relation_list(own_table_field, foreign_table_field.table) - field2 = Helper.get_field_in_n_m_relation_list(foreign_table_field, own_table_field.table) - text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute({ - "table_name": HelperGetNames.get_table_name(nm_table_name), - "field1": field1, - "table1": HelperGetNames.get_table_name(own_table_field.table), - "field2": field2, - "table2": HelperGetNames.get_table_name(foreign_table_field.table), - "list_of_keys": ", ".join([field1, field2]), - }) + def get_nm_table_for_n_m_relation_lists( + own_table_field: TableFieldType, foreign_table_field: TableFieldType + ) -> tuple[str, str]: + nm_table_name = HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ) + field1 = Helper.get_field_in_n_m_relation_list( + own_table_field, foreign_table_field.table + ) + field2 = Helper.get_field_in_n_m_relation_list( + foreign_table_field, own_table_field.table + ) + text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute( + { + "table_name": HelperGetNames.get_table_name(nm_table_name), + "field1": field1, + "table1": HelperGetNames.get_table_name(own_table_field.table), + "field2": field2, + "table2": HelperGetNames.get_table_name(foreign_table_field.table), + "list_of_keys": ", ".join([field1, field2]), + } + ) return nm_table_name, text @staticmethod - def get_gm_table_for_gm_nm_relation_lists(own_table_field: TableFieldType, foreign_table_fields: List[TableFieldType]) -> Tuple[str, str]: + def get_gm_table_for_gm_nm_relation_lists( + own_table_field: TableFieldType, foreign_table_fields: list[TableFieldType] + ) -> tuple[str, str]: gm_table_name = HelperGetNames.get_gm_table_name(own_table_field) - joined_table_names = "('" + "', '".join([foreign_table_field.table for foreign_table_field in foreign_table_fields]) + "')" + joined_table_names = ( + "('" + + "', '".join( + [ + foreign_table_field.table + for foreign_table_field in foreign_table_fields + ] + ) + + "')" + ) foreign_table_ref_lines = [] subst_dict = { "own_table_column": own_table_field.column[:-1], } for foreign_table_field in foreign_table_fields: subst_dict["foreign_table_name"] = foreign_table_field.table - foreign_table_ref_lines.append(Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict)) + foreign_table_ref_lines.append( + Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict) + ) - text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute({ - "table_name": HelperGetNames.get_table_name(gm_table_name), - "own_table_name": HelperGetNames.get_table_name(own_table_field.table), - "own_table_name_with_ref_column": f"{own_table_field.table}_{own_table_field.ref_column}", - "own_table_ref_column": own_table_field.ref_column, - "own_table_column": own_table_field.column[:-1], - "tuple_of_foreign_table_names": joined_table_names, - "foreign_table_ref_lines": "\n".join(foreign_table_ref_lines), - }) + text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute( + { + "table_name": HelperGetNames.get_table_name(gm_table_name), + "own_table_name": HelperGetNames.get_table_name(own_table_field.table), + "own_table_name_with_ref_column": f"{own_table_field.table}_{own_table_field.ref_column}", + "own_table_ref_column": own_table_field.ref_column, + "own_table_column": own_table_field.column[:-1], + "tuple_of_foreign_table_names": joined_table_names, + "foreign_table_ref_lines": "\n".join(foreign_table_ref_lines), + } + ) return gm_table_name, text @staticmethod - def get_field_in_n_m_relation_list(own_table_field: TableFieldType, foreign_table_name: str) -> str: + def get_field_in_n_m_relation_list( + own_table_field: TableFieldType, foreign_table_name: str + ) -> str: if own_table_field.table == foreign_table_name: return own_table_field.column[:-1] else: @@ -772,10 +837,10 @@ def get_field_in_n_m_relation_list(own_table_field: TableFieldType, foreign_tabl @staticmethod def get_initials( - table_name: str, fname: str, type_: str, fdata: Dict[str, Any] - ) -> Tuple[SubstDict, SchemaZoneTexts]: + table_name: str, fname: str, type_: str, fdata: dict[str, Any] + ) -> tuple[SubstDict, SchemaZoneTexts]: text: SchemaZoneTexts = {} - flist: List[str] = [ + flist: list[str] = [ cast(str, form[1]) for form in Formatter().parse(Helper.FIELD_TEMPLATE.template) ] @@ -802,21 +867,21 @@ def get_initials( f"{table_name}.{fname}: seems to be an invalid default value" ) if (minimum := fdata.get("minimum")) is not None: - subst[ - "minimum" - ] = f" CONSTRAINT minimum_{fname} CHECK ({fname} >= {minimum})" + subst["minimum"] = ( + f" CONSTRAINT minimum_{fname} CHECK ({fname} >= {minimum})" + ) if minLength := fdata.get("minLength"): - subst[ - "minLength" - ] = f" CONSTRAINT minLength_{fname} CHECK (char_length({fname}) >= {minLength})" + subst["minLength"] = ( + f" CONSTRAINT minLength_{fname} CHECK (char_length({fname}) >= {minLength})" + ) if comment := fdata.get("description"): - text[ - "alter_table" - ] = f"comment on column {HelperGetNames.get_table_name(table_name)}.{fname} is '{comment}';\n" + text["alter_table"] = ( + f"comment on column {HelperGetNames.get_table_name(table_name)}.{fname} is '{comment}';\n" + ) return subst, text @staticmethod - def get_cardinality(field: Optional[Dict[str, Any]]) -> Tuple[str, bool]: + def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: """ Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required, p=primary """ @@ -872,8 +937,8 @@ def get_cardinality(field: Optional[Dict[str, Any]]) -> Tuple[str, bool]: @staticmethod def check_relation_definitions( - own: TableFieldType, foreigns: List[TableFieldType] - ) -> Tuple[str, bool]: + own: TableFieldType, foreigns: list[TableFieldType] + ) -> tuple[str, bool]: error = False text = "" own_c, tmp_error = Helper.get_cardinality(own.field_def) @@ -904,7 +969,9 @@ def check_relation_definitions( return text, error @staticmethod - def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) -> bool: + def generate_field_view_or_nothing( + own: TableFieldType, foreign: TableFieldType + ) -> bool: """Decides, whether a relation field will be physical, view field or nothing Returns True = if necessary build table, view or intermediate Tables etc. False = do only extra @@ -953,7 +1020,9 @@ def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) if bool(own.field_def.get("reference", False)) == bool( foreign.field_def.get("reference", False) ): - if own.field_def.get("primary", False) == foreign.field_def.get("primary", False): + if own.field_def.get("primary", False) == foreign.field_def.get( + "primary", False + ): raise Exception( f"Type combination undecidable: {own_type}:{foreign_type} on field {own.collectionfield}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" ) @@ -966,20 +1035,27 @@ def generate_field_view_or_nothing(own: TableFieldType, foreign: TableFieldType) else: return own.field_def.get("required", False) elif result == "decide_alphabetical": - return foreign.collectionfield == "-" or own.collectionfield < foreign.collectionfield + return ( + foreign.collectionfield == "-" + or own.collectionfield < foreign.collectionfield + ) elif result == "decide_sql": if own.field_def.get("sql") or own.field_def.get("reference"): return True else: - raise Exception(f"Missing sql-or to-attribute for field {own.collectionfield}") + raise Exception( + f"Missing sql-or to-attribute for field {own.collectionfield}" + ) return cast(bool, result) @staticmethod - def get_generic_combined_fields(generic_plain_field_name: str, own_column: str, foreign_table:str) -> str: + def get_generic_combined_fields( + generic_plain_field_name: str, own_column: str, foreign_table: str + ) -> str: return f" {generic_plain_field_name} integer GENERATED ALWAYS AS (CASE WHEN split_part({own_column}, '/', 1) = '{foreign_table}' THEN cast(split_part({own_column}, '/', 2) AS INTEGER) ELSE null END) STORED,\n" @staticmethod - def get_generic_field_constraint(own_column:str, foreign_tables:List[str]) -> str: + def get_generic_field_constraint(own_column: str, foreign_tables: list[str]) -> str: return f""" CONSTRAINT valid_{own_column}_part1 CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" @@ -1007,11 +1083,11 @@ def _first_to_second(t1: str, t2: str) -> bool: @staticmethod def get_definitions_from_foreign( - to: Optional[str], reference: Optional[str] + to: str | None, reference: str | None ) -> TableFieldType: tname = "" fname = "" - tfield: Dict[str, Any] = {} + tfield: dict[str, Any] = {} ref_column = "" if to: tname, fname, tfield = ModelsHelper.get_field_definition_from_to(to) @@ -1027,9 +1103,9 @@ def get_definitions_from_foreign( def get_definitions_from_foreign_list( table: str, field: str, - to: Optional[Union[ToDict, List[str]]], - reference: Optional[List[str]], - ) -> List[TableFieldType]: + to: ToDict | list[str] | None, + reference: list[str] | None, + ) -> list[TableFieldType]: """ used for generic_relation with multiple foreign relations """ @@ -1037,7 +1113,7 @@ def get_definitions_from_foreign_list( raise Exception( f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" ) - results: List[TableFieldType] = [] + results: list[TableFieldType] = [] if isinstance(to, dict): fname = "/" + to["field"] for table in to["collections"]: @@ -1046,14 +1122,16 @@ def get_definitions_from_foreign_list( ) elif isinstance(to, list): for collectionfield in to: - results.append(ModelsHelper.get_definitions_from_foreign(collectionfield, None)) + results.append( + ModelsHelper.get_definitions_from_foreign(collectionfield, None) + ) elif reference: for ref in reference: results.append(ModelsHelper.get_definitions_from_foreign(None, ref)) return results @staticmethod - def get_field_definition_from_to(to: str) -> Tuple[str, str, Dict[str, Any]]: + def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: tname, fname = to.split("/") try: field = MODELS[tname][fname] @@ -1062,7 +1140,7 @@ def get_field_definition_from_to(to: str) -> Tuple[str, str, Dict[str, Any]]: return tname, fname, field -FIELD_TYPES: Dict[str, Dict[str, Any]] = { +FIELD_TYPES: dict[str, dict[str, Any]] = { "string": { "pg_type": string.Template("varchar(${maxLength})"), "method": GenerateCodeBlocks.get_schema_simple_types, @@ -1163,8 +1241,8 @@ def main() -> None: sys.exit(0) # Fix broken keys - models_yml = models_yml.replace(" yes:".encode(), ' "yes":'.encode()) - models_yml = models_yml.replace(" no:".encode(), ' "no":'.encode()) + models_yml = models_yml.replace(b" yes:", b' "yes":') + models_yml = models_yml.replace(b" no:", b' "no":') # Load and parse models.yml MODELS = yaml.safe_load(models_yml) @@ -1176,7 +1254,7 @@ def main() -> None: alter_table_code, final_info_code, missing_handled_attributes, - im_table_code + im_table_code, ) = GenerateCodeBlocks.generate_the_code() with open(DESTINATION, "w") as dest: dest.write(Helper.FILE_TEMPLATE) diff --git a/cli/libs/helper_get_names.py b/dev/src/helper_get_names.py similarity index 71% rename from cli/libs/helper_get_names.py rename to dev/src/helper_get_names.py index eb273df0..887cf5f3 100644 --- a/cli/libs/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -1,18 +1,19 @@ -from typing import Any, Dict, Optional +from typing import Any KEYSEPARATOR = "/" + class TableFieldType: def __init__( self, table: str, column: str, - field_def: Optional[Dict[str, Any]], + field_def: dict[str, Any] | None, ref_column: str = "id", ): self.table = table self.column = column - self.field_def: Dict[str, Any] = field_def or {} + self.field_def: dict[str, Any] = field_def or {} self.ref_column = ref_column @property @@ -29,8 +30,11 @@ class HelperGetNames: def max_length(func) -> str: def wrapper(*args, **kwargs): name = func(*args, **kwargs) - assert len(name) <= HelperGetNames.MAX_LEN, f"Generated name '{name}' to long in function {func}!" + assert ( + len(name) <= HelperGetNames.MAX_LEN + ), f"Generated name '{name}' to long in function {func}!" return name + return wrapper @staticmethod @@ -48,8 +52,10 @@ def get_view_name(table_name: str) -> str: @staticmethod @max_length def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: - """ table name n:m-relations intermediate table""" - if (own_str := f"{own.table}_{own.column}") < (foreign_str := f"{foreign.table}_{foreign.column}"): + """table name n:m-relations intermediate table""" + if (own_str := f"{own.table}_{own.column}") < ( + foreign_str := f"{foreign.table}_{foreign.column}" + ): return f"nm_{own_str}_{foreign.table}" else: return f"nm_{foreign_str}_{own.table}" @@ -57,7 +63,5 @@ def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: @staticmethod @max_length def get_gm_table_name(table_field: TableFieldType) -> str: - """ table name generic-list:m-relations intermediate table""" + """table name generic-list:m-relations intermediate table""" return f"gm_{table_field.table}_{table_field.column}" - - From 449bf0a55fde182ba147a4359887452b7ca41a99 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 8 Feb 2024 18:53:57 +0100 Subject: [PATCH 004/142] first doc and working for debugger --- dev/Dockerfile | 2 ++ dev/doc/generate_sql_schema.md | 36 +++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/dev/Dockerfile b/dev/Dockerfile index 20f1b85d..4680621e 100644 --- a/dev/Dockerfile +++ b/dev/Dockerfile @@ -7,5 +7,7 @@ WORKDIR /app/dev/ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt +EXPOSE 5678 + STOPSIGNAL SIGKILL CMD sleep infinity diff --git a/dev/doc/generate_sql_schema.md b/dev/doc/generate_sql_schema.md index 7e5e8ab3..efb68334 100644 --- a/dev/doc/generate_sql_schema.md +++ b/dev/doc/generate_sql_schema.md @@ -1,7 +1,41 @@ # Readme for relational sql schema -## Naming conventions for intermediate files +All names used in postgres are limited to 63 characters per name. This can only be changed by compiling the source code of postgres. +The length will be checked by the following methods on generating the sql-schema. + +## The type TableFieldType + +This type is used sometimes for the parameters of the methods. It can be build by it's constructor or as convinience with the static method **get_definitions_from_foreign**, which takes as parametes +the str-values from the **to**- and the **reference**-Attribut from models field description. + +It contains the **table_name**, **fname** for the field name, the dictionary **tfield** from the models.yml with all attributes and a **ref_column**, usually filled with **id** for the foreign-key definitions. +As convinience property there is the collectionfield as combination of table_name/field_name. + +## Naming conventions for tables and depending views as used in models.yml + +To get the table name use method **get_table_name** with the parameter of the collection name from the models.yml. The resulting name has a **T** for table to distinguish it from the view. + +To get the view name use the method **get_view_name** with the parameter of the collection name from the models.yml. The view name is identical with that from models.yml, except for **group and user**, which are reserved names in sql. They will get an appended **_** to their name. + + +## Naming conventions for intermediate tables and views ### relation-list versus relation-list +Because the base tables of these intermediate files are symmetric, the parts of the tables name are taken in an alphabetical order of their **collectionfields**. The name is build from parts + +* nm_ Constant part to mark a n:m intermediate table +* table_name of the smaller **collectionfield** +* _ Constant divider +* field name of the smaller **collectionfield** +* _ Constant divider +* table name of the greater **collectionfield** + ### generic-relation-list versus relation-list + +The name of the intermediate table for **generic-relation-list** versus **relation-list** is always build from the generic-relation-lists side. + +* gm_ Constant part to mark a genericc-list:m intermediate table +* table name of the generic-relation-list field +* _ Constant divider +* field name of the generic-relation-list field \ No newline at end of file From 90dee01d77c6029bece6a75f191c2fcb863c883a Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 12 Feb 2024 16:55:52 +0100 Subject: [PATCH 005/142] shorten projector. used_as_default_projector_for_current_list_of_speakers_in_meeting_id, len > 63 --- models.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models.yml b/models.yml index 822c8e90..45297d9e 100644 --- a/models.yml +++ b/models.yml @@ -1756,7 +1756,7 @@ meeting: required: true default_projector_current_list_of_speakers_ids: type: relation-list - to: projector/used_as_default_projector_for_current_list_of_speakers_in_meeting_id + to: projector/used_as_default_projector_for_current_los_in_meeting_id restriction_mode: B required: true default_projector_motion_ids: @@ -3757,7 +3757,7 @@ projector: type: relation to: meeting/default_projector_list_of_speakers_ids restriction_mode: A - used_as_default_projector_for_current_list_of_speakers_in_meeting_id: + used_as_default_projector_for_current_los_in_meeting_id: type: relation to: meeting/default_projector_current_list_of_speakers_ids restriction_mode: A From a9a028a3ae7f95a0780967c1403fd4047d6f891b Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 13 Feb 2024 16:01:05 +0100 Subject: [PATCH 006/142] table- and field-names moved to helper_get_names.py --- dev/sql/schema_relational.sql | 6 +- dev/src/generate_sql_schema.py | 131 ++++++--------------------- dev/src/helper_get_names.py | 156 +++++++++++++++++++++++++++++++-- 3 files changed, 183 insertions(+), 110 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index dedbb7e7..8800d593 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -2,7 +2,7 @@ -- schema.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'c942b45a22fcdb3b4d25726f48a411dc' +-- MODELS_YML_CHECKSUM = 'ed713236ea135e4f94c20b65ce2ce694' -- Type definitions DO $$ BEGIN @@ -2092,7 +2092,7 @@ SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id *** ntR:1t => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id *** ntR:1t => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id *** ntR:1t => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id -*** ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_list_of_speakers_in_meeting_id +*** ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id *** ntR:1t => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id *** ntR:1t => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id *** ntR:1t => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id @@ -2309,7 +2309,7 @@ SQL 1t:1tR => projector/used_as_reference_projector_meeting_id:-> meeting/refere *** 1t:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids *** 1t:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids *** 1t:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids -*** 1t:ntR => projector/used_as_default_projector_for_current_list_of_speakers_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids +*** 1t:ntR => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids *** 1t:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids *** 1t:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids *** 1t:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 4db1bbb4..4e9d1d3b 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1,6 +1,3 @@ -import hashlib -import os -import re import string import sys from collections import defaultdict @@ -12,9 +9,7 @@ from textwrap import dedent from typing import Any, TypedDict, cast -import requests -import yaml -from helper_get_names import HelperGetNames, TableFieldType +from helper_get_names import HelperGetNames, InternalHelper, TableFieldType SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() @@ -112,14 +107,6 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: continue schema_zone_texts: SchemaZoneTexts = defaultdict(str) # type: ignore cls.intermediate_tables = {} - # commented, because automatically expanded. Would be better to not expand automatically - # for reducing redundancy - # if table_name == "_meta": - # for enum_name, enum_data in fields.items(): - # pre_code += Helper.get_enum_type_definition( - # "", enum_name, enum_data - # ) - # continue for fname, fdata in fields.items(): for attr in fdata: @@ -236,8 +223,10 @@ def get_relation_type( ) -> SchemaZoneTexts: text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) - foreign_table_field: TableFieldType = ModelsHelper.get_definitions_from_foreign( - fdata.get("to"), fdata.get("reference") + foreign_table_field: TableFieldType = ( + TableFieldType.get_definitions_from_foreign( + fdata.get("to"), fdata.get("reference") + ) ) final_info, error = Helper.check_relation_definitions( own_table_field, [foreign_table_field] @@ -316,9 +305,11 @@ def get_relation_list_type( """ text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) - foreign_table_field: TableFieldType = ModelsHelper.get_definitions_from_foreign( - fdata.get("to"), - fdata.get("reference"), + foreign_table_field: TableFieldType = ( + TableFieldType.get_definitions_from_foreign( + fdata.get("to"), + fdata.get("reference"), + ) ) final_info, error = Helper.check_relation_definitions( own_table_field, [foreign_table_field] @@ -548,7 +539,6 @@ def get_generic_relation_list_type( class Helper: - ref_compiled = compiled = re.compile(r"(^\w+\b).*?\((.*?)\)") FILE_TEMPLATE = dedent( """ -- schema.sql for initial database setup OpenSlides @@ -599,7 +589,7 @@ class Helper: ) ) GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( - " ${own_table_column}_${foreign_table_name}_id integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_table_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}T(id)," + " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_table_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}T(id)," ) RELATION_LIST_AGENDA = dedent( @@ -676,22 +666,12 @@ def get_undecided_all(table_name: str, code: str) -> str: f"/*\n Fields without SQL definition for table {table_name}\n\n{code}\n*/\n" ) - @staticmethod - def get_enum_type_name( - table_name: str, - fname: str, - ) -> str: - if table_name: - return f"enum_{table_name}_{fname}" - else: - return f"enum_{fname}" - @staticmethod def get_enum_type_definition(table_name: str, fname: str, enum_: list[Any]) -> str: # enums per type are always strings in postgres enumeration = ", ".join([f"'{str(item)}'" for item in enum_]) subst = { - "enum_type": Helper.get_enum_type_name(table_name, fname), + "enum_type": HelperGetNames.get_enum_type_name(fname, table_name), "enumeration": enumeration, } return Helper.ENUM_DEFINITION_TEMPLATE.substitute(subst) @@ -770,10 +750,10 @@ def get_nm_table_for_n_m_relation_lists( nm_table_name = HelperGetNames.get_nm_table_name( own_table_field, foreign_table_field ) - field1 = Helper.get_field_in_n_m_relation_list( + field1 = HelperGetNames.get_field_in_n_m_relation_list( own_table_field, foreign_table_field.table ) - field2 = Helper.get_field_in_n_m_relation_list( + field2 = HelperGetNames.get_field_in_n_m_relation_list( foreign_table_field, own_table_field.table ) text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute( @@ -804,11 +784,14 @@ def get_gm_table_for_gm_nm_relation_lists( + "')" ) foreign_table_ref_lines = [] - subst_dict = { - "own_table_column": own_table_field.column[:-1], - } + own_table_column = own_table_field.column[:-1] for foreign_table_field in foreign_table_fields: - subst_dict["foreign_table_name"] = foreign_table_field.table + foreign_table_name = foreign_table_field.table + subst_dict = { + "own_table_column": own_table_column, + "foreign_table_name": foreign_table_name, + "gm_content_field": HelperGetNames.get_gm_content_field(own_table_column, foreign_table_name) + } foreign_table_ref_lines.append( Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict) ) @@ -826,15 +809,6 @@ def get_gm_table_for_gm_nm_relation_lists( ) return gm_table_name, text - @staticmethod - def get_field_in_n_m_relation_list( - own_table_field: TableFieldType, foreign_table_name: str - ) -> str: - if own_table_field.table == foreign_table_name: - return own_table_field.column[:-1] - else: - return f"{own_table_field.table}_id" - @staticmethod def get_initials( table_name: str, fname: str, type_: str, fdata: dict[str, Any] @@ -846,7 +820,7 @@ def get_initials( ] subst: SubstDict = cast(SubstDict, {k: "" for k in flist}) if (enum_ := fdata.get("enum")) or fdata.get("items"): - subst_type = Helper.get_enum_type_name(table_name, fname) + subst_type = HelperGetNames.get_enum_type_name(fname, table_name) if not enum_: subst_type += "[]" else: @@ -1056,7 +1030,8 @@ def get_generic_combined_fields( @staticmethod def get_generic_field_constraint(own_column: str, foreign_tables: list[str]) -> str: - return f""" CONSTRAINT valid_{own_column}_part1 CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" + constraint_name = HelperGetNames.get_generic_constraint_name(own_column) + return f""" CONSTRAINT {constraint_name} CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" class ModelsHelper: @@ -1070,7 +1045,7 @@ def is_fk_initially_deferred(own_table: str, foreign_table: str) -> bool: def _first_to_second(t1: str, t2: str) -> bool: for field in MODELS[t1].values(): if field.get("required") and field["type"].startswith("relation"): - ftable, _ = Helper.get_foreign_key_table_column( + ftable, _ = InternalHelper.get_foreign_key_table_column( field.get("to"), field.get("reference") ) if ftable == t2: @@ -1081,24 +1056,6 @@ def _first_to_second(t1: str, t2: str) -> bool: return _first_to_second(foreign_table, own_table) return False - @staticmethod - def get_definitions_from_foreign( - to: str | None, reference: str | None - ) -> TableFieldType: - tname = "" - fname = "" - tfield: dict[str, Any] = {} - ref_column = "" - if to: - tname, fname, tfield = ModelsHelper.get_field_definition_from_to(to) - ref_column = "id" - if reference: - tname, ref_column = Helper.get_foreign_key_table_column(to, reference) - # if not fname: - # fname = ref_column - # tfield = {"type": "relation", "reference": reference} - return TableFieldType(tname, fname, tfield, ref_column) - @staticmethod def get_definitions_from_foreign_list( table: str, @@ -1118,27 +1075,18 @@ def get_definitions_from_foreign_list( fname = "/" + to["field"] for table in to["collections"]: results.append( - ModelsHelper.get_definitions_from_foreign(table + fname, None) + TableFieldType.get_definitions_from_foreign(table + fname, None) ) elif isinstance(to, list): for collectionfield in to: results.append( - ModelsHelper.get_definitions_from_foreign(collectionfield, None) + TableFieldType.get_definitions_from_foreign(collectionfield, None) ) elif reference: for ref in reference: - results.append(ModelsHelper.get_definitions_from_foreign(None, ref)) + results.append(TableFieldType.get_definitions_from_foreign(None, ref)) return results - @staticmethod - def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: - tname, fname = to.split("/") - try: - field = MODELS[tname][fname] - except Exception as e: - field = {} - return tname, fname, field - FIELD_TYPES: dict[str, dict[str, Any]] = { "string": { @@ -1224,28 +1172,7 @@ def main() -> None: else: file = SOURCE - if os.path.isfile(file): - with open(file, "rb") as x: - models_yml = x.read() - else: - models_yml = requests.get(file).content - - # calc checksum to assert the schema.sql is up-to-date - checksum = hashlib.md5(models_yml).hexdigest() - - if len(sys.argv) > 1 and sys.argv[1] == "check": - from openslides_backend.models.models import MODELS_YML_CHECKSUM - - assert checksum == MODELS_YML_CHECKSUM - print("models.py is up to date (checksum-comparison)") - sys.exit(0) - - # Fix broken keys - models_yml = models_yml.replace(b" yes:", b' "yes":') - models_yml = models_yml.replace(b" no:", b' "no":') - - # Load and parse models.yml - MODELS = yaml.safe_load(models_yml) + MODELS, checksum = InternalHelper.read_models_yml(file) ( pre_code, diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 887cf5f3..ece498b6 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -1,4 +1,10 @@ -from typing import Any +import hashlib +import os +import re +from typing import Any, Callable + +import requests +import yaml KEYSEPARATOR = "/" @@ -23,12 +29,27 @@ def collectionfield(self) -> str: else: return "-" + @staticmethod + def get_definitions_from_foreign( + to: str | None, reference: str | None + ) -> "TableFieldType": + tname = "" + fname = "" + tfield: dict[str, Any] = {} + ref_column = "" + if to: + tname, fname, tfield = InternalHelper.get_field_definition_from_to(to) + ref_column = "id" + if reference: + tname, ref_column = InternalHelper.get_foreign_key_table_column(to, reference) + return TableFieldType(tname, fname, tfield, ref_column) + class HelperGetNames: MAX_LEN = 63 - def max_length(func) -> str: - def wrapper(*args, **kwargs): + def max_length(func: Callable) -> Callable: + def wrapper(*args, **kwargs) -> str: # type:ignore name = func(*args, **kwargs) assert ( len(name) <= HelperGetNames.MAX_LEN @@ -40,11 +61,13 @@ def wrapper(*args, **kwargs): @staticmethod @max_length def get_table_name(table_name: str) -> str: + """get's the table name as ol dcollection name with appendis 'T'""" return table_name + "T" @staticmethod @max_length def get_view_name(table_name: str) -> str: + """ get's the name of a view, usually the old collection name""" if table_name in ("group", "user"): return table_name + "_" return table_name @@ -52,7 +75,7 @@ def get_view_name(table_name: str) -> str: @staticmethod @max_length def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: - """table name n:m-relations intermediate table""" + """get's the table name n:m-relations intermediate table""" if (own_str := f"{own.table}_{own.column}") < ( foreign_str := f"{foreign.table}_{foreign.column}" ): @@ -63,5 +86,128 @@ def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: @staticmethod @max_length def get_gm_table_name(table_field: TableFieldType) -> str: - """table name generic-list:m-relations intermediate table""" + """get's th table name for generic-list:many-relations intermediate table""" return f"gm_{table_field.table}_{table_field.column}" + + @staticmethod + @max_length + def get_field_in_n_m_relation_list( + own_table_field: TableFieldType, foreign_table_name: str + ) -> str: + """ get's the field name in a n:m-intermediate table. + If both sides of the relation are in same table, the field name without 's' is used, + otherwise the related tables names are used + """ + if own_table_field.table == foreign_table_name: + return own_table_field.column[:-1] + else: + return f"{own_table_field.table}_id" + + @staticmethod + @max_length + def get_gm_content_field(table:str, field:str) -> str: + """ Gets the name of content field in an generic:many intermediate table""" + return f"{table}_{field}_id" + + @staticmethod + @max_length + def get_enum_type_name( + fname: str, + table_name: str, + ) -> str: + """ gets the name of an enum with prefix enum, table_name_name and fname""" + return f"enum_{table_name}_{fname}" + + @staticmethod + @max_length + def get_generic_constraint_name( + fname: str, + ) -> str: + """ gets the name of a generic constraint""" + return f"valid_{fname}_part1" + + + +class InternalHelper: + MODELS: dict[str, dict[str, Any]] = {} + checksum: str = "" + ref_compiled = compiled = re.compile(r"(^\w+\b).*?\((.*?)\)") + + @classmethod + def read_models_yml(cls, file: str) -> tuple[dict[str, Any], str]: + """ method reads modesl.yml from file or web and returns MODELS and it's checksum""" + if os.path.isfile(file): + with open(file, "rb") as x: + models_yml = x.read() + else: + models_yml = requests.get(file).content + + # calc checksum to assert the schema.sql is up-to-date + checksum = hashlib.md5(models_yml).hexdigest() + + # Fix broken keys + models_yml = models_yml.replace(b" yes:", b' "yes":') + models_yml = models_yml.replace(b" no:", b' "no":') + + # Load and parse models.yml + cls.MODELS = yaml.safe_load(models_yml) + cls.check_field_length() + return cls.MODELS, checksum + + @classmethod + def check_field_length(cls) -> None: + to_long: list[str] = [] + for table_name, fields in cls.MODELS.items(): + if table_name in ["_migration_index", "_meta"]: + continue + for fname in fields.keys(): + if len(fname) > HelperGetNames.MAX_LEN: + to_long.append(f"{table_name}.{fname}:{len(fname)}") + if to_long: + raise Exception("\n".join(to_long)) + + @staticmethod + def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: + tname, fname = to.split("/") + try: + field = InternalHelper.get_models(tname, fname) + except Exception: + raise Exception(f"Exception on splitting to {to} in get_field_definition_from_to") + assert (len(tname) <= HelperGetNames.MAX_LEN + ), f"Generated tname '{tname}' to long in function 'get_field_definition_from_to'!" + assert (len(fname) <= HelperGetNames.MAX_LEN + ), f"Generated fname '{fname}' to long in function 'get_field_definition_from_to'!" + + return tname, fname, field + + @staticmethod + def get_foreign_key_table_column( + to: str | None, reference: str | None + ) -> tuple[str, str]: + """ + Returns a tuple (table_name, field_name) gotten from "to" and/or "reference"-attribut + """ + if reference: + result = InternalHelper.ref_compiled.search(reference) + if result is None: + return reference.strip(), "id" + re_groups = result.groups() + cols = re_groups[1] + if cols: + cols = ",".join([col.strip() for col in cols.split(",")]) + else: + cols = "id" + return re_groups[0], cols + elif to: + return to.split("/")[0], "id" + else: + raise Exception("Relation field without reference or to") + + @classmethod + def get_models(cls, collection: str, field: str) -> dict[str, Any]: + if cls.MODELS: + try: + return cls.MODELS[collection][field] + except: + raise Exception(f"MODELS field {collection}.{field} doesn't exist") + raise Exception("You have to initialize models in class InternalHelper") \ No newline at end of file From f59d7d166430b4a7f232e9f053865c89ff30ea7c Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 13 Feb 2024 16:25:17 +0100 Subject: [PATCH 007/142] all constraint names to build in helper_get_names --- dev/sql/schema_relational.sql | 10 +++++----- dev/src/generate_sql_schema.py | 12 +++++++----- dev/src/helper_get_names.py | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 8800d593..937c64a0 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1347,7 +1347,7 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_idsT ( tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committeeT(id), tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meetingT(id), CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), - CONSTRAINT unique_organization_tag_id_tagged_id UNIQUE (organization_tag_id, tagged_id) + CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_userT ( @@ -1412,7 +1412,7 @@ CREATE TABLE IF NOT EXISTS gm_tag_tagged_idsT ( tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), - CONSTRAINT unique_tag_id_tagged_id UNIQUE (tag_id, tagged_id) + CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motionT ( @@ -1427,7 +1427,7 @@ CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_idsT ( state_extension_reference_id varchar(100) NOT NULL, state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), - CONSTRAINT unique_motion_id_state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) + CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_idsT ( @@ -1436,7 +1436,7 @@ CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_idsT ( recommendation_extension_reference_id varchar(100) NOT NULL, recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), - CONSTRAINT unique_motion_id_recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) + CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); CREATE TABLE IF NOT EXISTS nm_motion_state_next_state_ids_motion_stateT ( @@ -1459,7 +1459,7 @@ CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_idsT ( attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topicT(id), attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), - CONSTRAINT unique_mediafile_id_attachment_id UNIQUE (mediafile_id, attachment_id) + CONSTRAINT unique_$mediafile_id_$attachment_id UNIQUE (mediafile_id, attachment_id) ); CREATE TABLE IF NOT EXISTS nm_chat_group_read_group_ids_groupT ( diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 4e9d1d3b..2838f261 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -582,8 +582,8 @@ class Helper: ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}), ${own_table_column} varchar(100) NOT NULL, ${foreign_table_ref_lines} - CONSTRAINT valid_${own_table_column}_part1 CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), - CONSTRAINT unique_${own_table_name_with_ref_column}_${own_table_column} UNIQUE (${own_table_name_with_ref_column}, ${own_table_column}) + CONSTRAINT ${valid_constraint_name} CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), + CONSTRAINT ${unique_constraint_name} UNIQUE (${own_table_name_with_ref_column}, ${own_table_column}) ); """ ) @@ -800,11 +800,13 @@ def get_gm_table_for_gm_nm_relation_lists( { "table_name": HelperGetNames.get_table_name(gm_table_name), "own_table_name": HelperGetNames.get_table_name(own_table_field.table), - "own_table_name_with_ref_column": f"{own_table_field.table}_{own_table_field.ref_column}", + "own_table_name_with_ref_column": (own_table_name_with_ref_column := f"{own_table_field.table}_{own_table_field.ref_column}"), "own_table_ref_column": own_table_field.ref_column, - "own_table_column": own_table_field.column[:-1], + "own_table_column": own_table_column, "tuple_of_foreign_table_names": joined_table_names, "foreign_table_ref_lines": "\n".join(foreign_table_ref_lines), + "valid_constraint_name": HelperGetNames.get_generic_valid_constraint_name(own_table_column), + "unique_constraint_name": HelperGetNames.get_generic_unique_constraint_name(own_table_name_with_ref_column, own_table_column), } ) return gm_table_name, text @@ -1030,7 +1032,7 @@ def get_generic_combined_fields( @staticmethod def get_generic_field_constraint(own_column: str, foreign_tables: list[str]) -> str: - constraint_name = HelperGetNames.get_generic_constraint_name(own_column) + constraint_name = HelperGetNames.get_generic_valid_constraint_name(own_column) return f""" CONSTRAINT {constraint_name} CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index ece498b6..7aa8594d 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -120,12 +120,24 @@ def get_enum_type_name( @staticmethod @max_length - def get_generic_constraint_name( + def get_generic_valid_constraint_name( fname: str, ) -> str: - """ gets the name of a generic constraint""" + """ gets the name of a generic valid constraint""" return f"valid_{fname}_part1" + @staticmethod + @max_length + def get_generic_unique_constraint_name( + own_table_name_with_ref_column: str, own_table_column: str + ) -> str: + """ gets the name of a generic unique constraint + Params: + - {table_name}_{ref_column} + - {owcolumn} + """ + return f"unique_${own_table_name_with_ref_column}_${own_table_column}" + class InternalHelper: From 7f73d36689394e096db1a59df0361c65cdc9f560 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 14 Feb 2024 11:46:42 +0100 Subject: [PATCH 008/142] added .gitignore --- .gitignore | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..27a914ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Patterns of intentionally untracked files that Git should ignore + +# Private development data +private/ + +# Python compiled files +__pycache__/ + +# Python virtual environment target directory +.virtualenv/ +.venv/ + +# Mypy (static type checker) cache +.mypy_cache/ + +# Pytest, pytest-cov and Coverage.py cache +.pytest_cache/ +.coverage +htmlcov/ + +# Specific editor files +.vscode/* +.vimrc + +# coverage + +migrations/*.json +migrations/*.sql + +# secrets +secrets/ From 1ffc8dc213ac317484bd43ebcb4aea5984eb56bf Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 14 Feb 2024 18:10:36 +0100 Subject: [PATCH 009/142] last name get methos (?) --- dev/sql/schema_relational.sql | 2 +- dev/src/generate_sql_schema.py | 32 +++++++++++--------------------- dev/src/helper_get_names.py | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 937c64a0..ff413f79 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -436,7 +436,7 @@ comment on column organizationT.limit_of_users is 'Maximum of active users for t CREATE TABLE IF NOT EXISTS userT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, username varchar(256) NOT NULL, - saml_id varchar(256) CONSTRAINT minLength_saml_id CHECK (char_length(saml_id) >= 1), + saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), pronoun varchar(32), title varchar(256), first_name varchar(256), diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 2838f261..ac3c1443 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -843,12 +843,14 @@ def get_initials( f"{table_name}.{fname}: seems to be an invalid default value" ) if (minimum := fdata.get("minimum")) is not None: + minimum_constraint_name = HelperGetNames.get_minimum_constraint_name(fname) subst["minimum"] = ( - f" CONSTRAINT minimum_{fname} CHECK ({fname} >= {minimum})" + f" CONSTRAINT {minimum_constraint_name} CHECK ({fname} >= {minimum})" ) if minLength := fdata.get("minLength"): + minlength_constraint_name = HelperGetNames.get_minlength_constraint_name(fname) subst["minLength"] = ( - f" CONSTRAINT minLength_{fname} CHECK (char_length({fname}) >= {minLength})" + f" CONSTRAINT {minlength_constraint_name} CHECK (char_length({fname}) >= {minLength})" ) if comment := fdata.get("description"): text["alter_table"] = ( @@ -913,35 +915,23 @@ def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: @staticmethod def check_relation_definitions( - own: TableFieldType, foreigns: list[TableFieldType] + own_field: TableFieldType, foreign_fields: list[TableFieldType] ) -> tuple[str, bool]: error = False text = "" - own_c, tmp_error = Helper.get_cardinality(own.field_def) + own_c, tmp_error = Helper.get_cardinality(own_field.field_def) error = error or tmp_error foreigns_c = [] foreign_collectionfields = [] - for foreign in foreigns: - foreign_c, tmp_error = Helper.get_cardinality(foreign.field_def) + for foreign_field in foreign_fields: + foreign_c, tmp_error = Helper.get_cardinality(foreign_field.field_def) foreigns_c.append(foreign_c) error = error or tmp_error - foreign_collectionfields.append(foreign.collectionfield) - - # if table_field["type"] == "relation": - # if foreign_field and foreign_field.get("type") == "relation": - # if (("sql" in table_field) == ("sql" in foreign_field)) and ( - # ("required" in table_field) == ("required" in foreign_field) - # ): - # error = True - # elif table_field["type"] == "relation-list": - # if foreign_field and foreign_field.get("type") == "relation": - # if ( - # not (table_field.get("sql")) or (foreign_field.get("to")) - # ) or table_field["required"]: - # error = True + foreign_collectionfields.append(foreign_field.collectionfield) + if error: text = "*** " - text += f"{own_c}:{','.join(foreigns_c)} => {own.collectionfield}:-> {','.join(foreign_collectionfields)}\n" + text += f"{own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" return text, error @staticmethod diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 7aa8594d..deb180eb 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -138,6 +138,21 @@ def get_generic_unique_constraint_name( """ return f"unique_${own_table_name_with_ref_column}_${own_table_column}" + @staticmethod + @max_length + def get_minimum_constraint_name( + fname: str, + ) -> str: + """ gets the name of minimum constraint""" + return f"minimum_{fname}" + + @staticmethod + @max_length + def get_minlength_constraint_name( + fname: str, + ) -> str: + """ gets the name of minLength constraint""" + return f"minlength_{fname}" class InternalHelper: From e569cda951c74a4eeadb03a1ab8738d20a83efe9 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 14 Feb 2024 18:29:33 +0100 Subject: [PATCH 010/142] removed primary attribute --- dev/sql/schema_relational.sql | 77 +++++++++++++++++----------------- dev/src/generate_sql_schema.py | 17 ++------ models.yml | 54 ++++++++---------------- 3 files changed, 60 insertions(+), 88 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index ff413f79..c09fcfbb 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -2,7 +2,7 @@ -- schema.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'ed713236ea135e4f94c20b65ce2ce694' +-- MODELS_YML_CHECKSUM = 'ca02291a1b40ccea8a47867a55eafe4c' -- Type definitions DO $$ BEGIN @@ -1969,7 +1969,6 @@ Field Attributes:Field Attributes opposite side s: sql directive given, but must be generated s+: sql directive includive sql-statement R: Required - p: primary set for deciding field/sql Model.Field -> Model.Field model.field names */ @@ -2014,7 +2013,7 @@ FIELD nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1tR => committee/meeting_ids:-> meeting/committee_id -FIELD 1tp:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id +FIELD 1r: => committee/default_meeting_id:-> meeting/ SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids @@ -2065,24 +2064,24 @@ SQL nt:1tR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_i SQL nt:1tR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1tR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1tR => meeting/chat_message_ids:-> chat_message/meeting_id -FIELD 1tp:1t => meeting/logo_projector_main_id:-> mediafile/used_as_logo_projector_main_in_meeting_id -FIELD 1tp:1t => meeting/logo_projector_header_id:-> mediafile/used_as_logo_projector_header_in_meeting_id -FIELD 1tp:1t => meeting/logo_web_header_id:-> mediafile/used_as_logo_web_header_in_meeting_id -FIELD 1tp:1t => meeting/logo_pdf_header_l_id:-> mediafile/used_as_logo_pdf_header_l_in_meeting_id -FIELD 1tp:1t => meeting/logo_pdf_header_r_id:-> mediafile/used_as_logo_pdf_header_r_in_meeting_id -FIELD 1tp:1t => meeting/logo_pdf_footer_l_id:-> mediafile/used_as_logo_pdf_footer_l_in_meeting_id -FIELD 1tp:1t => meeting/logo_pdf_footer_r_id:-> mediafile/used_as_logo_pdf_footer_r_in_meeting_id -FIELD 1tp:1t => meeting/logo_pdf_ballot_paper_id:-> mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id -FIELD 1tp:1t => meeting/font_regular_id:-> mediafile/used_as_font_regular_in_meeting_id -FIELD 1tp:1t => meeting/font_italic_id:-> mediafile/used_as_font_italic_in_meeting_id -FIELD 1tp:1t => meeting/font_bold_id:-> mediafile/used_as_font_bold_in_meeting_id -FIELD 1tp:1t => meeting/font_bold_italic_id:-> mediafile/used_as_font_bold_italic_in_meeting_id -FIELD 1tp:1t => meeting/font_monospace_id:-> mediafile/used_as_font_monospace_in_meeting_id -FIELD 1tp:1t => meeting/font_chyron_speaker_name_id:-> mediafile/used_as_font_chyron_speaker_name_in_meeting_id -FIELD 1tp:1t => meeting/font_projector_h1_id:-> mediafile/used_as_font_projector_h1_in_meeting_id -FIELD 1tp:1t => meeting/font_projector_h2_id:-> mediafile/used_as_font_projector_h2_in_meeting_id +FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ +FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ +FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ +FIELD 1r: => meeting/font_regular_id:-> mediafile/ +FIELD 1r: => meeting/font_italic_id:-> mediafile/ +FIELD 1r: => meeting/font_bold_id:-> mediafile/ +FIELD 1r: => meeting/font_bold_italic_id:-> mediafile/ +FIELD 1r: => meeting/font_monospace_id:-> mediafile/ +FIELD 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ +FIELD 1r: => meeting/font_projector_h1_id:-> mediafile/ +FIELD 1r: => meeting/font_projector_h2_id:-> mediafile/ FIELD 1tR:nt => meeting/committee_id:-> committee/meeting_ids -SQL 1t:1tp => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id +SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id @@ -2104,11 +2103,11 @@ SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id *** ntR:1t => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id *** ntR:1t => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id -FIELD 1tp:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id +FIELD 1r: => meeting/admin_group_id:-> group/ SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1tR => group/default_group_for_meeting_id:-> meeting/default_group_id -SQL 1t:1tp => group/admin_group_for_meeting_id:-> meeting/admin_group_id +SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids @@ -2285,22 +2284,22 @@ SQL 1t:1GtR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_ SQL nt:1GtR => mediafile/projection_ids:-> projection/content_object_id FIELD nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids FIELD 1GtR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids -SQL 1t:1tp => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id -SQL 1t:1tp => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id -SQL 1t:1tp => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id -SQL 1t:1tp => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id -SQL 1t:1tp => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id -SQL 1t:1tp => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id -SQL 1t:1tp => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id -SQL 1t:1tp => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id -SQL 1t:1tp => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id -SQL 1t:1tp => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id -SQL 1t:1tp => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id -SQL 1t:1tp => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id -SQL 1t:1tp => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id -SQL 1t:1tp => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id -SQL 1t:1tp => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id -SQL 1t:1tp => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id +SQL 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id +SQL 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id +SQL 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id +SQL 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id +SQL 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id +SQL 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id +SQL 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id +SQL 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id +SQL 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id +SQL 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id +SQL 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id +SQL 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id +SQL 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id +SQL 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id +SQL 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id +SQL 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id SQL nt:1t => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1t => projector/preview_projection_ids:-> projection/preview_projector_id @@ -2347,4 +2346,4 @@ FIELD 1tR:nt => chat_message/meeting_id:-> meeting/chat_message_ids */ -/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, primary */ \ No newline at end of file +/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields */ \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index ac3c1443..99dbe6a5 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -610,7 +610,6 @@ class Helper: s: sql directive given, but must be generated s+: sql directive includive sql-statement R: Required - p: primary set for deciding field/sql Model.Field -> Model.Field model.field names */ @@ -861,7 +860,7 @@ def get_initials( @staticmethod def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: """ - Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required, p=primary + Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) """ if field: required = bool(field.get("required")) @@ -869,7 +868,6 @@ def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: sql_empty = field.get("sql") == "" to = bool(field.get("to")) reference = bool(field.get("reference")) - primary = bool(field.get("primary")) # general rules of inconsistent field descriptions on field level error = ( @@ -906,8 +904,6 @@ def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: result += "-" if required: result += "R" - if primary: - result += "p" else: result = "" error = False @@ -986,14 +982,9 @@ def generate_field_view_or_nothing( if bool(own.field_def.get("reference", False)) == bool( foreign.field_def.get("reference", False) ): - if own.field_def.get("primary", False) == foreign.field_def.get( - "primary", False - ): - raise Exception( - f"Type combination undecidable: {own_type}:{foreign_type} on field {own.collectionfield}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" - ) - else: - return own.field_def.get("primary", False) + raise Exception( + f"Type combination undecidable: {own_type}:{foreign_type} on field {own.collectionfield}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" + ) else: return bool(own.field_def.get("reference", False)) else: diff --git a/models.yml b/models.yml index 45297d9e..fd6b1732 100644 --- a/models.yml +++ b/models.yml @@ -673,9 +673,8 @@ committee: restriction_mode: A default_meeting_id: type: relation - to: meeting/default_meeting_for_committee_id + reference: meeting(id) restriction_mode: A - primary: True user_ids: type: number[] to: user/committee_ids @@ -1619,84 +1618,68 @@ meeting: # Logos and Fonts logo_projector_main_id: type: relation - to: mediafile/used_as_logo_projector_main_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_projector_header_id: type: relation - to: mediafile/used_as_logo_projector_header_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_web_header_id: type: relation - to: mediafile/used_as_logo_web_header_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_pdf_header_l_id: type: relation - to: mediafile/used_as_logo_pdf_header_l_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_pdf_header_r_id: type: relation - to: mediafile/used_as_logo_pdf_header_r_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_pdf_footer_l_id: type: relation - to: mediafile/used_as_logo_pdf_footer_l_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_pdf_footer_r_id: type: relation - to: mediafile/used_as_logo_pdf_footer_r_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True logo_pdf_ballot_paper_id: type: relation - to: mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_regular_id: type: relation - to: mediafile/used_as_font_regular_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_italic_id: type: relation - to: mediafile/used_as_font_italic_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_bold_id: type: relation - to: mediafile/used_as_font_bold_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_bold_italic_id: type: relation - to: mediafile/used_as_font_bold_italic_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_monospace_id: type: relation - to: mediafile/used_as_font_monospace_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_chyron_speaker_name_id: type: relation - to: mediafile/used_as_font_chyron_speaker_name_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_projector_h1_id: type: relation - to: mediafile/used_as_font_projector_h1_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True font_projector_h2_id: type: relation - to: mediafile/used_as_font_projector_h2_in_meeting_id + reference: mediafile(id) restriction_mode: B - primary: True # Other relations committee_id: type: relation @@ -1816,9 +1799,8 @@ meeting: restriction_mode: B admin_group_id: type: relation - to: group/admin_group_for_meeting_id + reference: group(id) restriction_mode: B - primary: True group: id: From d33c92c578d705a81294c52fdd326bbdcca3d89e Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 15 Feb 2024 11:17:40 +0100 Subject: [PATCH 011/142] fix error DEFAULT with empty enum list --- dev/sql/schema_relational.sql | 2 +- dev/src/generate_sql_schema.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index c09fcfbb..9d9111f6 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1001,7 +1001,7 @@ CREATE TABLE IF NOT EXISTS motion_stateT ( recommendation_label varchar(256), is_internal_recommendation boolean, css_class enum_motion_state_css_class NOT NULL DEFAULT 'lightblue', - restrictions enum_motion_state_restrictions[] DEFAULT '{""}', + restrictions enum_motion_state_restrictions[] DEFAULT '{}', allow_support boolean DEFAULT False, allow_create_poll boolean DEFAULT False, allow_submitter_edit boolean DEFAULT False, diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 99dbe6a5..6f7e2d00 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -835,7 +835,7 @@ def get_initials( elif isinstance(default, (int, bool, float)): subst["default"] = f" DEFAULT {default}" elif isinstance(default, list): - tmp = '{"' + '", "'.join(default) + '"}' + tmp = '{"' + '", "'.join(default) + '"}' if default else '{}' subst["default"] = f" DEFAULT '{tmp}'" else: raise Exception( From dff00aa29abf41f1446fbd14a5bc8558dad8f92b Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 15 Feb 2024 12:15:15 +0100 Subject: [PATCH 012/142] schema.sql to schema_relational.sql in text --- dev/sql/schema_relational.sql | 2 +- dev/src/generate_sql_schema.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 9d9111f6..c175f21a 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,5 +1,5 @@ --- schema.sql for initial database setup OpenSlides +-- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. -- MODELS_YML_CHECKSUM = 'ca02291a1b40ccea8a47867a55eafe4c' diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 6f7e2d00..92c3c5ba 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -541,7 +541,7 @@ def get_generic_relation_list_type( class Helper: FILE_TEMPLATE = dedent( """ - -- schema.sql for initial database setup OpenSlides + -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. """ @@ -1144,7 +1144,7 @@ def get_definitions_from_foreign_list( def main() -> None: """ - Main entry point for this script to generate the schema.sql from models.yml. + Main entry point for this script to generate the schema_relational.sql from models.yml. """ global MODELS From 59e452f4f8fe66cd477a8395b5c2c5d64188c5ac Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 15 Feb 2024 12:15:50 +0100 Subject: [PATCH 013/142] make target generate-relational-schema --- dev/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/Makefile b/dev/Makefile index 67e35a73..1e9ca518 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -32,6 +32,9 @@ mypy: validate-models: python src/validate.py +generate-relational-schema: + python src/generate_sql_schema.py + # Docker manage commands run-dev: From 8a740ed5aa34eee192d364ab3ad285a9ab18099d Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 16 Feb 2024 14:32:07 +0100 Subject: [PATCH 014/142] makefile commands db handling --- dev/Dockerfile | 2 +- dev/Makefile | 14 ++++++++++++++ dev/docker-compose.yml | 7 +++++++ dev/requirements.txt | 1 + dev/scripts/apply_db_schema.sh | 3 +++ 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100755 dev/scripts/apply_db_schema.sh diff --git a/dev/Dockerfile b/dev/Dockerfile index 4680621e..d04ca658 100644 --- a/dev/Dockerfile +++ b/dev/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.10.13-slim-bookworm -RUN apt-get update && apt-get install --yes make bash-completion +RUN apt-get update && apt-get install --yes make bash-completion postgresql-client WORKDIR /app/dev/ diff --git a/dev/Makefile b/dev/Makefile index 1e9ca518..37c4c011 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -35,6 +35,20 @@ validate-models: generate-relational-schema: python src/generate_sql_schema.py +drop-database: + dropdb -f -e --if-exists -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} ${POSTGRES_DB} + +create-database: + createdb -e -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} ${POSTGRES_DB} + +apply-db-schema: + scripts/apply_db_schema.sh + +create-database-with-schema: drop-database create-database apply-db-schema + +run-psql: + psql -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} -d ${POSTGRES_DB} + # Docker manage commands run-dev: diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index 04e6f91e..537cfb54 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -7,6 +7,13 @@ services: - 5678:5678 volumes: - ..:/app + environment: + - POSTGRES_HOST=postgres + - POSTGRES_PORT=5432 + - POSTGRES_USER=openslides + - POSTGRES_DB=openslides + - PGPASSWORD=openslides + depends_on: - postgres networks: diff --git a/dev/requirements.txt b/dev/requirements.txt index b660bdb6..6a040bf5 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -9,6 +9,7 @@ pyyaml==6.0.1 simplejson==3.19.2 debugpy==1.8.0 requests==2.31.0 +psycopg2==2.9.9 # typing types-PyYAML==6.0.12.12 diff --git a/dev/scripts/apply_db_schema.sh b/dev/scripts/apply_db_schema.sh new file mode 100755 index 00000000..11f7e297 --- /dev/null +++ b/dev/scripts/apply_db_schema.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +psql -1 -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f sql/schema_relational.sql From 308f3a1ac276918ea090a10295f6db77dbb0d1ec Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 19 Feb 2024 09:26:55 +0100 Subject: [PATCH 015/142] use psycopg-binaray for dev --- dev/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/requirements.txt b/dev/requirements.txt index 6a040bf5..bea282f2 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -9,7 +9,7 @@ pyyaml==6.0.1 simplejson==3.19.2 debugpy==1.8.0 requests==2.31.0 -psycopg2==2.9.9 +psycopg2-binary==2.9.9 # only dev, in production use psycopg2 # typing types-PyYAML==6.0.12.12 From d5e43e278f214a8f3fcc9ea6a146388024cceb7d Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 19 Feb 2024 10:29:15 +0100 Subject: [PATCH 016/142] Apply database schema on starting dev container --- dev/Dockerfile | 2 ++ dev/scripts/entrypoint.sh | 6 ++++++ dev/scripts/wait-for-database.sh | 7 +++++++ 3 files changed, 15 insertions(+) create mode 100755 dev/scripts/entrypoint.sh create mode 100755 dev/scripts/wait-for-database.sh diff --git a/dev/Dockerfile b/dev/Dockerfile index d04ca658..7393dc12 100644 --- a/dev/Dockerfile +++ b/dev/Dockerfile @@ -10,4 +10,6 @@ RUN pip install --no-cache-dir -r requirements.txt EXPOSE 5678 STOPSIGNAL SIGKILL + +ENTRYPOINT ["scripts/entrypoint.sh"] CMD sleep infinity diff --git a/dev/scripts/entrypoint.sh b/dev/scripts/entrypoint.sh new file mode 100755 index 00000000..d0c0b31e --- /dev/null +++ b/dev/scripts/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +scripts/wait-for-database.sh +scripts/apply_db_schema.sh + +exec "$@" \ No newline at end of file diff --git a/dev/scripts/wait-for-database.sh b/dev/scripts/wait-for-database.sh new file mode 100755 index 00000000..18a7d51c --- /dev/null +++ b/dev/scripts/wait-for-database.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +until pg_isready -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER"; do + echo "Waiting for Postgres server '$POSTGRES_HOST' to become available..." + sleep 3 +done + From 25947b6bb9213835fd8151f6d5d135674ab4e5f6 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 23 Feb 2024 10:06:41 +0100 Subject: [PATCH 017/142] intermediate with postgres function truncate_tables for test setup, ca 6.6 seconds --- dev/requirements.txt | 2 +- dev/sql/schema_relational.sql | 166 +++++++++++++++-- dev/src/generate_sql_schema.py | 34 +++- dev/src/helper_get_names.py | 49 ++--- dev/tests/__init__.py | 0 dev/tests/base.py | 277 ++++++++++++++++++++++++++++ dev/tests/test_generic_relations.py | 24 +++ 7 files changed, 507 insertions(+), 45 deletions(-) create mode 100644 dev/tests/__init__.py create mode 100644 dev/tests/base.py create mode 100644 dev/tests/test_generic_relations.py diff --git a/dev/requirements.txt b/dev/requirements.txt index 51f8ea98..355a73b3 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -9,7 +9,7 @@ pyyaml==6.0.1 simplejson==3.19.2 debugpy==1.8.0 requests==2.31.0 -psycopg2-binary==2.9.9 # only dev, in production use psycopg2 +psycopg[binary]==3.1.18 # only dev, in production use psycopg # typing types-PyYAML==6.0.12.12 diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index c175f21a..30eed386 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -2,7 +2,19 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'ca02291a1b40ccea8a47867a55eafe4c' +CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ +DECLARE + statements CURSOR FOR + SELECT tablename FROM pg_tables + WHERE tableowner = username AND schemaname = 'public'; +BEGIN + FOR stmt IN statements LOOP + EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' RESTART IDENTITY CASCADE;'; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +-- MODELS_YML_CHECKSUM = 'd0cede9d5ce9ee2baf1ec49ea4cb214a' -- Type definitions DO $$ BEGIN @@ -241,7 +253,7 @@ END$$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_group_permissions') THEN - CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see'); + CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see'); ELSE RAISE NOTICE 'type "enum_group_permissions" already exists, skipping'; END IF; @@ -259,7 +271,7 @@ END$$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_speaker_speech_state') THEN - CREATE TYPE enum_speaker_speech_state AS ENUM ('contribution', 'pro', 'contra'); + CREATE TYPE enum_speaker_speech_state AS ENUM ('contribution', 'pro', 'contra', 'intervention', 'interposed_question'); ELSE RAISE NOTICE 'type "enum_speaker_speech_state" already exists, skipping'; END IF; @@ -449,7 +461,6 @@ CREATE TABLE IF NOT EXISTS userT ( gender varchar(256), email varchar(256), default_number varchar(256), - default_structure_level varchar(256), default_vote_weight decimal(6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', last_email_sent timestamptz, is_demo_user boolean, @@ -471,7 +482,6 @@ CREATE TABLE IF NOT EXISTS meeting_userT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, comment text, number varchar(256), - structure_level varchar(256), about_me text, vote_weight decimal(6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), user_id integer NOT NULL, @@ -629,6 +639,9 @@ CREATE TABLE IF NOT EXISTS meetingT ( list_of_speakers_can_set_contribution_self boolean DEFAULT False, list_of_speakers_speaker_note_for_everyone boolean DEFAULT True, list_of_speakers_initially_closed boolean DEFAULT False, + list_of_speakers_default_structure_level_time integer CONSTRAINT minimum_list_of_speakers_default_structure_level_time CHECK (list_of_speakers_default_structure_level_time >= 0), + list_of_speakers_enable_interposed_question boolean, + list_of_speakers_intervention_time integer, motions_default_workflow_id integer NOT NULL, motions_default_amendment_workflow_id integer NOT NULL, motions_default_statute_amendment_workflow_id integer NOT NULL, @@ -740,9 +753,22 @@ This email was generated automatically.', comment on column meetingT.external_id is 'unique in committee'; comment on column meetingT.is_active_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meetingT.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column meetingT.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; +comment on column meetingT.list_of_speakers_intervention_time is '0 disables intervention speakers.'; comment on column meetingT.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; +CREATE TABLE IF NOT EXISTS structure_levelT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name varchar(256) NOT NULL, + color integer CHECK (color >= 0 and color <= 16777215), + default_time integer CONSTRAINT minimum_default_time CHECK (default_time >= 0), + meeting_id integer NOT NULL +); + + + + CREATE TABLE IF NOT EXISTS groupT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), @@ -791,6 +817,7 @@ CREATE TABLE IF NOT EXISTS agenda_itemT ( closed boolean DEFAULT False, type enum_agenda_item_type DEFAULT 'common', duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), + moderator_notes text, is_internal boolean, is_hidden boolean, level integer, @@ -832,6 +859,25 @@ CREATE TABLE IF NOT EXISTS list_of_speakersT ( comment on column list_of_speakersT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +CREATE TABLE IF NOT EXISTS structure_level_list_of_speakersT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + structure_level_id integer NOT NULL, + list_of_speakers_id integer NOT NULL, + initial_time integer NOT NULL CONSTRAINT minimum_initial_time CHECK (initial_time >= 1), + additional_time real, + remaining_time real NOT NULL, + current_start_time timestamptz, + meeting_id integer NOT NULL +); + + + +comment on column structure_level_list_of_speakersT.initial_time is 'The initial time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakersT.additional_time is 'The summed added time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakersT.remaining_time is 'The currently remaining time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakersT.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; + + CREATE TABLE IF NOT EXISTS point_of_order_categoryT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, text varchar(256) NOT NULL, @@ -846,11 +892,15 @@ CREATE TABLE IF NOT EXISTS speakerT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, begin_time timestamptz, end_time timestamptz, + pause_time timestamptz, + unpause_time timestamptz, + total_pause integer, weight integer DEFAULT 10000, speech_state enum_speaker_speech_state, note varchar(250), point_of_order boolean, list_of_speakers_id integer NOT NULL, + structure_level_list_of_speakers_id integer, meeting_user_id integer, point_of_order_category_id integer, meeting_id integer NOT NULL @@ -899,8 +949,6 @@ CREATE TABLE IF NOT EXISTS motionT ( recommendation_id integer, category_id integer, block_id integer, - editor_id integer, - working_group_speaker_id integer, statute_paragraph_id integer, meeting_id integer NOT NULL ); @@ -922,6 +970,28 @@ CREATE TABLE IF NOT EXISTS motion_submitterT ( +CREATE TABLE IF NOT EXISTS motion_editorT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight integer, + meeting_user_id integer NOT NULL, + motion_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE IF NOT EXISTS motion_working_group_speakerT ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + weight integer, + meeting_user_id integer NOT NULL, + motion_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + CREATE TABLE IF NOT EXISTS motion_commentT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, comment text, @@ -999,7 +1069,7 @@ CREATE TABLE IF NOT EXISTS motion_stateT ( name varchar(256) NOT NULL, weight integer NOT NULL, recommendation_label varchar(256), - is_internal_recommendation boolean, + is_internal boolean, css_class enum_motion_state_css_class NOT NULL DEFAULT 'lightblue', restrictions enum_motion_state_restrictions[] DEFAULT '{}', allow_support boolean DEFAULT False, @@ -1340,6 +1410,12 @@ CREATE TABLE IF NOT EXISTS nm_meeting_user_supported_motion_ids_motionT ( PRIMARY KEY (meeting_user_id, motion_id) ); +CREATE TABLE IF NOT EXISTS nm_meeting_user_structure_level_ids_structure_levelT ( + meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), + structure_level_id integer NOT NULL REFERENCES structure_levelT (id), + PRIMARY KEY (meeting_user_id, structure_level_id) +); + CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_idsT ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, organization_tag_id integer NOT NULL REFERENCES organization_tagT(id), @@ -1504,13 +1580,14 @@ CREATE OR REPLACE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_noteT p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id) from speakerT s where s.meeting_user_id = m.id) as speaker_ids, (select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motionT n where n.meeting_user_id = m.id) as supported_motion_ids, -(select array_agg(m1.id) from motionT m1 where m1.editor_id = m.id) as editor_for_motion_ids, -(select array_agg(m1.id) from motionT m1 where m1.working_group_speaker_id = m.id) as working_group_speaker_for_motion_ids, +(select array_agg(me.id) from motion_editorT me where me.meeting_user_id = m.id) as motion_editor_ids, +(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, (select array_agg(ms.id) from motion_submitterT ms where ms.meeting_user_id = m.id) as motion_submitter_ids, (select array_agg(a.id) from assignment_candidateT a where a.meeting_user_id = m.id) as assignment_candidate_ids, (select array_agg(mu.id) from meeting_userT mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, (select array_agg(c.id) from chat_messageT c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_userT n where n.meeting_user_id = m.id) as group_ids +(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_userT n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_levelT n where n.meeting_user_id = m.id) as structure_level_ids FROM meeting_userT m; @@ -1548,6 +1625,7 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(t.id) from tagT t where t.meeting_id = m.id) as tag_ids, (select array_agg(a.id) from agenda_itemT a where a.meeting_id = m.id) as agenda_item_ids, (select array_agg(l.id) from list_of_speakersT l where l.meeting_id = m.id) as list_of_speakers_ids, +(select array_agg(s.id) from structure_level_list_of_speakersT s where s.meeting_id = m.id) as structure_level_list_of_speakers_ids, (select array_agg(p.id) from point_of_order_categoryT p where p.meeting_id = m.id) as point_of_order_category_ids, (select array_agg(s.id) from speakerT s where s.meeting_id = m.id) as speaker_ids, (select array_agg(t.id) from topicT t where t.meeting_id = m.id) as topic_ids, @@ -1562,6 +1640,8 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(ms.id) from motion_statute_paragraphT ms where ms.meeting_id = m.id) as motion_statute_paragraph_ids, (select array_agg(mc.id) from motion_commentT mc where mc.meeting_id = m.id) as motion_comment_ids, (select array_agg(ms.id) from motion_submitterT ms where ms.meeting_id = m.id) as motion_submitter_ids, +(select array_agg(me.id) from motion_editorT me where me.meeting_id = m.id) as motion_editor_ids, +(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, (select array_agg(mc.id) from motion_change_recommendationT mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, (select array_agg(ms.id) from motion_stateT ms where ms.meeting_id = m.id) as motion_state_ids, (select array_agg(p.id) from pollT p where p.meeting_id = m.id) as poll_ids, @@ -1572,6 +1652,7 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(p.id) from personal_noteT p where p.meeting_id = m.id) as personal_note_ids, (select array_agg(c.id) from chat_groupT c where c.meeting_id = m.id) as chat_group_ids, (select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, +(select array_agg(s.id) from structure_levelT s where s.meeting_id = m.id) as structure_level_ids, (select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.tagged_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, @@ -1579,6 +1660,12 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, FROM meetingT m; +CREATE OR REPLACE VIEW structure_level AS SELECT *, +(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_levelT n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(sl.id) from structure_level_list_of_speakersT sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids +FROM structure_levelT s; + + CREATE OR REPLACE VIEW group_ AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_userT n where n.group_id = g.id) as meeting_user_ids, (select m.id from meetingT m where m.default_group_id = g.id) as default_group_for_meeting_id, @@ -1607,10 +1694,16 @@ FROM agenda_itemT a; CREATE OR REPLACE VIEW list_of_speakers AS SELECT *, (select array_agg(s.id) from speakerT s where s.list_of_speakers_id = l.id) as speaker_ids, +(select array_agg(s.id) from structure_level_list_of_speakersT s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, (select array_agg(p.content_object_id) from projectionT p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids FROM list_of_speakersT l; +CREATE OR REPLACE VIEW structure_level_list_of_speakers AS SELECT *, +(select array_agg(s1.id) from speakerT s1 where s1.structure_level_list_of_speakers_id = s.id) as speaker_ids +FROM structure_level_list_of_speakersT s; + + CREATE OR REPLACE VIEW point_of_order_category AS SELECT *, (select array_agg(s.id) from speakerT s where s.point_of_order_category_id = p.id) as speaker_ids FROM point_of_order_categoryT p; @@ -1637,6 +1730,8 @@ CREATE OR REPLACE VIEW motion AS SELECT *, (select array_agg(g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_idsT g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id) from motion_submitterT ms where ms.motion_id = m.id) as submitter_ids, (select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motionT n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(me.id) from motion_editorT me where me.motion_id = m.id) as editor_ids, +(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.content_object_id) from pollT p where p.content_object_id_motion_id = m.id) as poll_ids, (select array_agg(o.content_object_id) from optionT o where o.content_object_id_motion_id = m.id) as option_ids, (select array_agg(mc.id) from motion_change_recommendationT mc where mc.motion_id = m.id) as change_recommendation_ids, @@ -1816,6 +1911,8 @@ ALTER TABLE meetingT ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_cou ALTER TABLE meetingT ADD FOREIGN KEY(default_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; ALTER TABLE meetingT ADD FOREIGN KEY(admin_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; +ALTER TABLE structure_levelT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + ALTER TABLE groupT ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; ALTER TABLE groupT ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; ALTER TABLE groupT ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; @@ -1842,9 +1939,14 @@ ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_topic_id) REFERE ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafileT(id); ALTER TABLE list_of_speakersT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(structure_level_id) REFERENCES structure_levelT(id); +ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakersT(id); +ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + ALTER TABLE point_of_order_categoryT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE speakerT ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakersT(id); +ALTER TABLE speakerT ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakersT(id); ALTER TABLE speakerT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); ALTER TABLE speakerT ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_categoryT(id); ALTER TABLE speakerT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); @@ -1859,8 +1961,6 @@ ALTER TABLE motionT ADD FOREIGN KEY(state_id) REFERENCES motion_stateT(id); ALTER TABLE motionT ADD FOREIGN KEY(recommendation_id) REFERENCES motion_stateT(id); ALTER TABLE motionT ADD FOREIGN KEY(category_id) REFERENCES motion_categoryT(id); ALTER TABLE motionT ADD FOREIGN KEY(block_id) REFERENCES motion_blockT(id); -ALTER TABLE motionT ADD FOREIGN KEY(editor_id) REFERENCES meeting_userT(id); -ALTER TABLE motionT ADD FOREIGN KEY(working_group_speaker_id) REFERENCES meeting_userT(id); ALTER TABLE motionT ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraphT(id); ALTER TABLE motionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); @@ -1868,6 +1968,14 @@ ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_user_id) REFERENCES meetin ALTER TABLE motion_submitterT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +ALTER TABLE motion_editorT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE motion_editorT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); +ALTER TABLE motion_editorT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + +ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); +ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); +ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); + ALTER TABLE motion_commentT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); ALTER TABLE motion_commentT ADD FOREIGN KEY(section_id) REFERENCES motion_comment_sectionT(id); ALTER TABLE motion_commentT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); @@ -1999,14 +2107,15 @@ FIELD 1tR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids SQL nt:1tR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id SQL nt:1t => meeting_user/speaker_ids:-> speaker/meeting_user_id SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids -SQL nt:1t => meeting_user/editor_for_motion_ids:-> motion/editor_id -SQL nt:1t => meeting_user/working_group_speaker_for_motion_ids:-> motion/working_group_speaker_id +SQL nt:1tR => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id +SQL nt:1tR => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id SQL nt:1tR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id SQL nt:1t => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id FIELD 1t:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1t => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id SQL nt:1tR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids +SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids FIELD nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids @@ -2040,6 +2149,7 @@ SQL nt:1tR => meeting/projector_countdown_ids:-> projector_countdown/meeting_id SQL nt:1tR => meeting/tag_ids:-> tag/meeting_id SQL nt:1tR => meeting/agenda_item_ids:-> agenda_item/meeting_id SQL nt:1tR => meeting/list_of_speakers_ids:-> list_of_speakers/meeting_id +SQL nt:1tR => meeting/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/meeting_id SQL nt:1tR => meeting/point_of_order_category_ids:-> point_of_order_category/meeting_id SQL nt:1tR => meeting/speaker_ids:-> speaker/meeting_id SQL nt:1tR => meeting/topic_ids:-> topic/meeting_id @@ -2054,6 +2164,8 @@ SQL nt:1tR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id SQL nt:1tR => meeting/motion_statute_paragraph_ids:-> motion_statute_paragraph/meeting_id SQL nt:1tR => meeting/motion_comment_ids:-> motion_comment/meeting_id SQL nt:1tR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id +SQL nt:1tR => meeting/motion_editor_ids:-> motion_editor/meeting_id +SQL nt:1tR => meeting/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_id SQL nt:1tR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id SQL nt:1tR => meeting/motion_state_ids:-> motion_state/meeting_id SQL nt:1tR => meeting/poll_ids:-> poll/meeting_id @@ -2064,6 +2176,7 @@ SQL nt:1tR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_i SQL nt:1tR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1tR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1tR => meeting/chat_message_ids:-> chat_message/meeting_id +SQL nt:1tR => meeting/structure_level_ids:-> structure_level/meeting_id FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ @@ -2105,6 +2218,10 @@ SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id FIELD 1r: => meeting/admin_group_id:-> group/ +SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids +SQL nt:1tR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id +FIELD 1tR:nt => structure_level/meeting_id:-> meeting/structure_level_ids + SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1tR => group/default_group_for_meeting_id:-> meeting/default_group_id SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id @@ -2137,13 +2254,20 @@ FIELD 1tR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids FIELD 1GtR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,mediafile/list_of_speakers_id SQL nt:1tR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id +SQL nt:1tR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id SQL nt:1GtR => list_of_speakers/projection_ids:-> projection/content_object_id FIELD 1tR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids +FIELD 1tR:nt => structure_level_list_of_speakers/structure_level_id:-> structure_level/structure_level_list_of_speakers_ids +FIELD 1tR:nt => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/structure_level_list_of_speakers_ids +SQL nt:1t => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id +FIELD 1tR:nt => structure_level_list_of_speakers/meeting_id:-> meeting/structure_level_list_of_speakers_ids + FIELD 1tR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids SQL nt:1t => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id FIELD 1tR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids +FIELD 1t:nt => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/speaker_ids FIELD 1t:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids FIELD 1t:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids FIELD 1tR:nt => speaker/meeting_id:-> meeting/speaker_ids @@ -2174,8 +2298,8 @@ FIELD 1t:nt => motion/category_id:-> motion_category/motion_ids FIELD 1t:nt => motion/block_id:-> motion_block/motion_ids SQL nt:1tR => motion/submitter_ids:-> motion_submitter/motion_id SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ids -FIELD 1t:nt => motion/editor_id:-> meeting_user/editor_for_motion_ids -FIELD 1t:nt => motion/working_group_speaker_id:-> meeting_user/working_group_speaker_for_motion_ids +SQL nt:1tR => motion/editor_ids:-> motion_editor/motion_id +SQL nt:1tR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id SQL nt:1GtR => motion/poll_ids:-> poll/content_object_id SQL nt:1Gt => motion/option_ids:-> option/content_object_id SQL nt:1tR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id @@ -2193,6 +2317,14 @@ FIELD 1tR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitte FIELD 1tR:nt => motion_submitter/motion_id:-> motion/submitter_ids FIELD 1tR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids +FIELD 1tR:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids +FIELD 1tR:nt => motion_editor/motion_id:-> motion/editor_ids +FIELD 1tR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids + +FIELD 1tR:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids +FIELD 1tR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids +FIELD 1tR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids + FIELD 1tR:nt => motion_comment/motion_id:-> motion/comment_ids FIELD 1tR:nt => motion_comment/section_id:-> motion_comment_section/comment_ids FIELD 1tR:nt => motion_comment/meeting_id:-> meeting/motion_comment_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 92c3c5ba..bee07d7e 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -544,6 +544,18 @@ class Helper: -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. + CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ + DECLARE + statements CURSOR FOR + SELECT tablename FROM pg_tables + WHERE tableowner = username AND schemaname = 'public'; + BEGIN + FOR stmt IN statements LOOP + EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' RESTART IDENTITY CASCADE;'; + END LOOP; + END; + $$ LANGUAGE plpgsql; + """ ) FIELD_TEMPLATE = string.Template( @@ -789,7 +801,9 @@ def get_gm_table_for_gm_nm_relation_lists( subst_dict = { "own_table_column": own_table_column, "foreign_table_name": foreign_table_name, - "gm_content_field": HelperGetNames.get_gm_content_field(own_table_column, foreign_table_name) + "gm_content_field": HelperGetNames.get_gm_content_field( + own_table_column, foreign_table_name + ), } foreign_table_ref_lines.append( Helper.GM_FOREIGN_TABLE_LINE_TEMPLATE.substitute(subst_dict) @@ -799,13 +813,19 @@ def get_gm_table_for_gm_nm_relation_lists( { "table_name": HelperGetNames.get_table_name(gm_table_name), "own_table_name": HelperGetNames.get_table_name(own_table_field.table), - "own_table_name_with_ref_column": (own_table_name_with_ref_column := f"{own_table_field.table}_{own_table_field.ref_column}"), + "own_table_name_with_ref_column": ( + own_table_name_with_ref_column := f"{own_table_field.table}_{own_table_field.ref_column}" + ), "own_table_ref_column": own_table_field.ref_column, "own_table_column": own_table_column, "tuple_of_foreign_table_names": joined_table_names, "foreign_table_ref_lines": "\n".join(foreign_table_ref_lines), - "valid_constraint_name": HelperGetNames.get_generic_valid_constraint_name(own_table_column), - "unique_constraint_name": HelperGetNames.get_generic_unique_constraint_name(own_table_name_with_ref_column, own_table_column), + "valid_constraint_name": HelperGetNames.get_generic_valid_constraint_name( + own_table_column + ), + "unique_constraint_name": HelperGetNames.get_generic_unique_constraint_name( + own_table_name_with_ref_column, own_table_column + ), } ) return gm_table_name, text @@ -835,7 +855,7 @@ def get_initials( elif isinstance(default, (int, bool, float)): subst["default"] = f" DEFAULT {default}" elif isinstance(default, list): - tmp = '{"' + '", "'.join(default) + '"}' if default else '{}' + tmp = '{"' + '", "'.join(default) + '"}' if default else "{}" subst["default"] = f" DEFAULT '{tmp}'" else: raise Exception( @@ -847,7 +867,9 @@ def get_initials( f" CONSTRAINT {minimum_constraint_name} CHECK ({fname} >= {minimum})" ) if minLength := fdata.get("minLength"): - minlength_constraint_name = HelperGetNames.get_minlength_constraint_name(fname) + minlength_constraint_name = HelperGetNames.get_minlength_constraint_name( + fname + ) subst["minLength"] = ( f" CONSTRAINT {minlength_constraint_name} CHECK (char_length({fname}) >= {minLength})" ) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index deb180eb..765fab7f 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -1,7 +1,8 @@ import hashlib import os import re -from typing import Any, Callable +from collections.abc import Callable +from typing import Any import requests import yaml @@ -41,7 +42,9 @@ def get_definitions_from_foreign( tname, fname, tfield = InternalHelper.get_field_definition_from_to(to) ref_column = "id" if reference: - tname, ref_column = InternalHelper.get_foreign_key_table_column(to, reference) + tname, ref_column = InternalHelper.get_foreign_key_table_column( + to, reference + ) return TableFieldType(tname, fname, tfield, ref_column) @@ -67,7 +70,7 @@ def get_table_name(table_name: str) -> str: @staticmethod @max_length def get_view_name(table_name: str) -> str: - """ get's the name of a view, usually the old collection name""" + """get's the name of a view, usually the old collection name""" if table_name in ("group", "user"): return table_name + "_" return table_name @@ -94,7 +97,7 @@ def get_gm_table_name(table_field: TableFieldType) -> str: def get_field_in_n_m_relation_list( own_table_field: TableFieldType, foreign_table_name: str ) -> str: - """ get's the field name in a n:m-intermediate table. + """get's the field name in a n:m-intermediate table. If both sides of the relation are in same table, the field name without 's' is used, otherwise the related tables names are used """ @@ -105,8 +108,8 @@ def get_field_in_n_m_relation_list( @staticmethod @max_length - def get_gm_content_field(table:str, field:str) -> str: - """ Gets the name of content field in an generic:many intermediate table""" + def get_gm_content_field(table: str, field: str) -> str: + """Gets the name of content field in an generic:many intermediate table""" return f"{table}_{field}_id" @staticmethod @@ -115,7 +118,7 @@ def get_enum_type_name( fname: str, table_name: str, ) -> str: - """ gets the name of an enum with prefix enum, table_name_name and fname""" + """gets the name of an enum with prefix enum, table_name_name and fname""" return f"enum_{table_name}_{fname}" @staticmethod @@ -123,7 +126,7 @@ def get_enum_type_name( def get_generic_valid_constraint_name( fname: str, ) -> str: - """ gets the name of a generic valid constraint""" + """gets the name of a generic valid constraint""" return f"valid_{fname}_part1" @staticmethod @@ -131,10 +134,10 @@ def get_generic_valid_constraint_name( def get_generic_unique_constraint_name( own_table_name_with_ref_column: str, own_table_column: str ) -> str: - """ gets the name of a generic unique constraint - Params: - - {table_name}_{ref_column} - - {owcolumn} + """gets the name of a generic unique constraint + Params: + - {table_name}_{ref_column} + - {owcolumn} """ return f"unique_${own_table_name_with_ref_column}_${own_table_column}" @@ -143,7 +146,7 @@ def get_generic_unique_constraint_name( def get_minimum_constraint_name( fname: str, ) -> str: - """ gets the name of minimum constraint""" + """gets the name of minimum constraint""" return f"minimum_{fname}" @staticmethod @@ -151,7 +154,7 @@ def get_minimum_constraint_name( def get_minlength_constraint_name( fname: str, ) -> str: - """ gets the name of minLength constraint""" + """gets the name of minLength constraint""" return f"minlength_{fname}" @@ -162,7 +165,7 @@ class InternalHelper: @classmethod def read_models_yml(cls, file: str) -> tuple[dict[str, Any], str]: - """ method reads modesl.yml from file or web and returns MODELS and it's checksum""" + """method reads modesl.yml from file or web and returns MODELS and it's checksum""" if os.path.isfile(file): with open(file, "rb") as x: models_yml = x.read() @@ -199,11 +202,15 @@ def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: try: field = InternalHelper.get_models(tname, fname) except Exception: - raise Exception(f"Exception on splitting to {to} in get_field_definition_from_to") - assert (len(tname) <= HelperGetNames.MAX_LEN - ), f"Generated tname '{tname}' to long in function 'get_field_definition_from_to'!" - assert (len(fname) <= HelperGetNames.MAX_LEN - ), f"Generated fname '{fname}' to long in function 'get_field_definition_from_to'!" + raise Exception( + f"Exception on splitting to {to} in get_field_definition_from_to" + ) + assert ( + len(tname) <= HelperGetNames.MAX_LEN + ), f"Generated tname '{tname}' to long in function 'get_field_definition_from_to'!" + assert ( + len(fname) <= HelperGetNames.MAX_LEN + ), f"Generated fname '{fname}' to long in function 'get_field_definition_from_to'!" return tname, fname, field @@ -237,4 +244,4 @@ def get_models(cls, collection: str, field: str) -> dict[str, Any]: return cls.MODELS[collection][field] except: raise Exception(f"MODELS field {collection}.{field} doesn't exist") - raise Exception("You have to initialize models in class InternalHelper") \ No newline at end of file + raise Exception("You have to initialize models in class InternalHelper") diff --git a/dev/tests/__init__.py b/dev/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dev/tests/base.py b/dev/tests/base.py new file mode 100644 index 00000000..07608b0e --- /dev/null +++ b/dev/tests/base.py @@ -0,0 +1,277 @@ +import os +from datetime import datetime +from typing import Any +from unittest import TestCase + +import psycopg +import pytest + +# ADMIN_USERNAME = "admin" +# ADMIN_PASSWORD = "admin" +#db_connection: psycopg.Connection = None + +class BaseTestCase(TestCase): + db_connection: psycopg.Connection = None + curs: psycopg.Cursor = None + + @classmethod + def setup_class(cls): + """setup any state specific to the execution of the given class (which + usually contains tests). + """ + start = datetime.now() + env = os.environ + try: + cls.db_connection = psycopg.connect(f"dbname='{env['POSTGRES_DB']}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'") + except Exception as e: + raise Exception(f"Cannot connect to database: {e.message}") + cls.db_connection.autocommit = False + print(f"class setup: {datetime.now() - start}") + + + @classmethod + def teardown_class(cls): + """teardown any state that was previously setup with a call to + setup_class. + """ + start = datetime.now() + cls.db_connection.close() + print(f"class teardown: {datetime.now() - start}") + + def setUp(self) -> None: + start = datetime.now() + #global db_connection + super().setUp() + env = os.environ + self.curs = self.db_connection.cursor() + self.curs.execute(f"select truncate_tables('{env['POSTGRES_USER']}');") + self.db_connection.commit() + print(f"test setup: {datetime.now() - start}") + + def tearDown(self) -> None: + start = datetime.now() + super().tearDown() + self.curs.close() + print(f"test teardown: {datetime.now() - start}") + + # self.created_fqids = set() + # self.create_model( + # "user/1", + # { + # "username": ADMIN_USERNAME, + # "password": self.auth.hash(ADMIN_PASSWORD), + # "default_password": ADMIN_PASSWORD, + # "is_active": True, + # "organization_management_level": "superadmin", + # "organization_id": ONE_ORGANIZATION_ID, + # }, + # ) + # self.create_model( + # ONE_ORGANIZATION_FQID, + # { + # "name": "OpenSlides Organization", + # "default_language": "en", + # "user_ids": [1], + # }, + # ) + # self.client = self.create_client(self.update_vote_service_auth_data) + # if self.auth_data: + # # Reuse old login data to avoid a new login request + # self.client.update_auth_data(self.auth_data) + # else: + # # Login and save copy of auth data for all following tests + # self.client.login(ADMIN_USERNAME, ADMIN_PASSWORD) + # BaseSystemTestCase.auth_data = deepcopy(self.client.auth_data) + # self.anon_client = self.create_client() + + + #@pytest.fixture(scope="session", autouse=True) + def setup_db_connect(self): + start = datetime.now() + global db_connection + env = os.environ + try: + db_connection = psycopg.connect(f"dbname='{env['POSTGRES_DB']}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'") + except Exception as e: + raise Exception(f"Cannot connect to database: {e.message}") + db_connection.autocommit = False + print(f"session setup: {datetime.now() - start}") + yield db_connection + start = datetime.now() + db_connection.close() + print(f"session teardown: {datetime.now() - start}") + + # def load_example_data(self) -> None: + # """ + # Useful for debug purposes when an action fails with the example data. + # Do NOT use in final tests since it takes a long time. + # """ + # example_data = get_initial_data_file(EXAMPLE_DATA_FILE) + # self._load_data(example_data) + + # def load_json_data(self, filename: str) -> None: + # """ + # Useful for debug purposes when an action fails with a specific dump. + # Do NOT use in final tests since it takes a long time. + # """ + # with open(filename) as file: + # data = json.loads(file.read()) + # self._load_data(data) + + # def _load_data(self, raw_data: dict[str, dict[str, Any]]) -> None: + # data = {} + # for collection, models in raw_data.items(): + # if collection == "_migration_index": + # continue + # for model_id, model in models.items(): + # data[f"{collection}/{model_id}"] = { + # f: v for f, v in model.items() if not f.startswith("meta_") + # } + # self.set_models(data) + + # def create_client( + # self, on_auth_data_changed: Callable[[AuthData], None] | None = None + # ) -> Client: + # return Client(self.app, on_auth_data_changed) + + # def login(self, user_id: int) -> None: + # """ + # Login the given user by fetching the default password from the datastore. + # """ + # user = self.get_model(f"user/{user_id}") + # assert user.get("default_password") + # self.client.login(user["username"], user["default_password"]) + + # def update_vote_service_auth_data(self, auth_data: AuthData) -> None: + # self.vote_service.set_authentication( + # auth_data["access_token"], auth_data["refresh_id"] + # ) + + # def get_application(self) -> WSGIApplication: + # raise NotImplementedError() + + # def assert_status_code(self, response: Response, code: int) -> None: + # if ( + # response.status_code != code + # and response.json + # and response.json.get("message") + # ): + # print(response.json) + # self.assertEqual(response.status_code, code) + + # def create_model( + # self, fqid: str, data: dict[str, Any] = {}, deleted: bool = False + # ) -> None: + # write_request = self.get_write_request( + # self.get_create_events(fqid, data, deleted) + # ) + # self.datastore.write(write_request) + + # def update_model(self, fqid: str, data: dict[str, Any]) -> None: + # write_request = self.get_write_request(self.get_update_events(fqid, data)) + # self.datastore.write(write_request) + + # def get_create_events( + # self, fqid: str, data: dict[str, Any] = {}, deleted: bool = False + # ) -> list[Event]: + # self.created_fqids.add(fqid) + # data["id"] = id_from_fqid(fqid) + # self.validate_fields(fqid, data) + # events = [Event(type=EventType.Create, fqid=fqid, fields=data)] + # if deleted: + # events.append(Event(type=EventType.Delete, fqid=fqid)) + # return events + + # def get_update_events(self, fqid: str, data: dict[str, Any]) -> list[Event]: + # self.validate_fields(fqid, data) + # return [Event(type=EventType.Update, fqid=fqid, fields=data)] + + # def get_write_request(self, events: list[Event]) -> WriteRequest: + # return WriteRequest(events, user_id=0) + + # def set_models(self, models: dict[str, dict[str, Any]]) -> None: + # """ + # Can be used to set multiple models at once, independent of create or update. + # Uses self.created_fqids to determine which models are already created. If you want to update + # a model which was not set in the test but created via an action, you may have to add the + # fqid to this set. + # """ + # events: list[Event] = [] + # for fqid, model in models.items(): + # if fqid in self.created_fqids: + # events.extend(self.get_update_events(fqid, model)) + # else: + # events.extend(self.get_create_events(fqid, model)) + # write_request = self.get_write_request(events) + # self.datastore.write(write_request) + + # def validate_fields(self, fqid: str, fields: dict[str, Any]) -> None: + # model = model_registry[collection_from_fqid(fqid)]() + # for field_name, value in fields.items(): + # try: + # model.get_field(field_name).validate_with_schema( + # fqid, field_name, value + # ) + # except ActionException as e: + # raise JsonSchemaException(e.message) + + # @with_database_context + # def get_model(self, fqid: str) -> dict[str, Any]: + # model = self.datastore.get( + # fqid, + # mapped_fields=[], + # get_deleted_models=DeletedModelsBehaviour.ALL_MODELS, + # lock_result=False, + # use_changed_models=False, + # ) + # self.assertTrue(model) + # self.assertEqual(model.get("id"), id_from_fqid(fqid)) + # return model + + # def assert_model_exists( + # self, fqid: str, fields: dict[str, Any] | None = None + # ) -> dict[str, Any]: + # return self._assert_fields(fqid, (fields or {}) | {"meta_deleted": False}) + + # def assert_model_not_exists(self, fqid: str) -> None: + # with self.assertRaises(DatastoreException): + # self.get_model(fqid) + + # def assert_model_deleted( + # self, fqid: str, fields: dict[str, Any] | None = None + # ) -> dict[str, Any]: + # return self._assert_fields(fqid, (fields or {}) | {"meta_deleted": True}) + + # def _assert_fields( + # self, fqid: FullQualifiedId, fields: dict[str, Any] + # ) -> dict[str, Any]: + # model = self.get_model(fqid) + # model_cls = model_registry[collection_from_fqid(fqid)]() + # for field_name, value in fields.items(): + # if not is_reserved_field(field_name) and value is not None: + # # assert that the field actually exists to detect errors in the tests + # model_cls.get_field(field_name) + # self.assertEqual( + # model.get(field_name), + # value, + # f"Models differ in field {field_name}!", + # ) + # return model + + # def assert_defaults(self, model: type[Model], instance: dict[str, Any]) -> None: + # for field in model().get_fields(): + # if getattr(field, "default", None) is not None: + # self.assertEqual( + # field.default, + # instance.get(field.own_field_name), + # f"Field {field.own_field_name}: Value {instance.get(field.own_field_name, 'None')} is not equal default value {field.default}.", + # ) + + # @with_database_context + # def assert_model_count(self, collection: str, meeting_id: int, count: int) -> None: + # db_count = self.datastore.count( + # collection, + # FilterOperator("meeting_id", "=", meeting_id), + # lock_result=False, + # ) + # self.assertEqual(db_count, count) diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py new file mode 100644 index 00000000..c6a8a709 --- /dev/null +++ b/dev/tests/test_generic_relations.py @@ -0,0 +1,24 @@ +from datetime import datetime + +import pytest +from tests.base import BaseTestCase # , db_connection + + +#@pytest.mark.usefixtures("setup_db_connect") +class GenericRelations(BaseTestCase): + def test_1(self) -> None: + print("test1 ohne daten") + assert 0 + + def test_2(self) -> None: + start = datetime.now() + with self.db_connection.transaction(): + for i in range(100): + result = self.curs.execute("insert into themeT (name, accent_500, primary_500, warn_500) VALUES (%s, %s, %s, %s)", (f"name{i}", i, i*10, i*100)) + print(f"test2 100 Sätze per Loop: {datetime.now() - start}") + assert 0 + + def test_3(self) -> None: + print("test3 ohne daten") + assert 0 + From b8fd2c3787516574a3339157b0f8abc4327fb0ea Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 29 Feb 2024 18:03:55 +0100 Subject: [PATCH 018/142] deployable schema and test with some data initialized --- dev/sql/schema_relational.sql | 13 +- dev/src/db_utils.py | 54 ++ dev/src/generate_sql_schema.py | 5 +- dev/src/helper_get_names.py | 2 +- dev/tests/base.py | 816 +++++++++++++++++++--------- dev/tests/test_generic_relations.py | 15 +- models.yml | 14 +- 7 files changed, 644 insertions(+), 275 deletions(-) create mode 100644 dev/src/db_utils.py diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 30eed386..4509987b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -14,7 +14,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'd0cede9d5ce9ee2baf1ec49ea4cb214a' +-- MODELS_YML_CHECKSUM = '665a7daf1eac5b0731787068b449e0b4' -- Type definitions DO $$ BEGIN @@ -929,6 +929,7 @@ CREATE TABLE IF NOT EXISTS motionT ( sequential_number integer NOT NULL, title varchar(256) NOT NULL, text text, + text_hash varchar(256), amendment_paragraphs jsonb, modified_final_version text, reason text, @@ -958,6 +959,12 @@ CREATE TABLE IF NOT EXISTS motionT ( comment on column motionT.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; comment on column motionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +/* + Fields without SQL definition for table motion + + identical_motion_ids type:int[] no method defined + +*/ CREATE TABLE IF NOT EXISTS motion_submitterT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, @@ -1992,7 +1999,7 @@ ALTER TABLE motion_change_recommendationT ADD FOREIGN KEY(meeting_id) REFERENCES ALTER TABLE motion_stateT ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_stateT(id); ALTER TABLE motion_stateT ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE motion_stateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +ALTER TABLE motion_stateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; ALTER TABLE motion_workflowT ADD FOREIGN KEY(first_state_id) REFERENCES motion_stateT(id) INITIALLY DEFERRED; ALTER TABLE motion_workflowT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; @@ -2478,4 +2485,4 @@ FIELD 1tR:nt => chat_message/meeting_id:-> meeting/chat_message_ids */ -/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields */ \ No newline at end of file +/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, deferred */ \ No newline at end of file diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py new file mode 100644 index 00000000..def70d52 --- /dev/null +++ b/dev/src/db_utils.py @@ -0,0 +1,54 @@ +from typing import Any + +from psycopg import Cursor, sql + + +class DbUtils: + @classmethod + def insert_wrapper(cls, curs: Cursor, table_name: str, data: dict[str, Any]) -> int: + query = f"INSERT INTO {table_name} ({', '.join(data.keys())}) VALUES({{}}) RETURNING id;" + query = ( + sql.SQL(query) + .format( + sql.SQL(", ").join(sql.Placeholder() * len(data.keys())), + ) + .as_string(curs) + ) + return curs.execute(query, tuple(data.values())).fetchone()["id"] + + @classmethod + def insert_many_wrapper( + cls, curs: Cursor, table_name: str, data_list: list[dict[str, Any]] + ) -> list[int]: + ids: list[int] = [] + if not data_list: + return ids + # use all keys in same sequence + keys = set() + for data in data_list: + keys.update(data.keys()) + keys = sorted(keys) + temp_data = {k: None for k in keys} + + dates = [temp_data | data for data in data_list] + query = ( + f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}) RETURNING id;" + ) + query = ( + sql.SQL(query) + .format( + sql.SQL(", ").join(sql.Placeholder() * len(keys)), + ) + .as_string(curs) + ) + curs.executemany( + query, + tuple(tuple(v for _, v in sorted(data.items())) for data in dates), + returning=True, + ) + ids = [] + while True: + ids.append(curs.fetchone()["id"]) + if not curs.nextset(): + break + return ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index bee07d7e..9eb5a20e 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -53,6 +53,7 @@ class SubstDict(TypedDict, total=False): minimum: str minLength: str enum_: str + deferred: str class GenerateCodeBlocks: @@ -239,7 +240,9 @@ def get_relation_type( text.update( cls.get_schema_simple_types(table_name, fname, fdata, "number") ) - initially_deferred = ModelsHelper.is_fk_initially_deferred( + initially_deferred = fdata.get( + "deferred" + ) or ModelsHelper.is_fk_initially_deferred( table_name, foreign_table_field.table ) text["alter_table_final"] = ( diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 765fab7f..68385d7e 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -242,6 +242,6 @@ def get_models(cls, collection: str, field: str) -> dict[str, Any]: if cls.MODELS: try: return cls.MODELS[collection][field] - except: + except KeyError: raise Exception(f"MODELS field {collection}.{field} doesn't exist") raise Exception("You have to initialize models in class InternalHelper") diff --git a/dev/tests/base.py b/dev/tests/base.py index 07608b0e..7e1415a9 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -5,273 +5,589 @@ import psycopg import pytest +from psycopg import sql +from psycopg.types.json import Jsonb +from src.db_utils import DbUtils # ADMIN_USERNAME = "admin" # ADMIN_PASSWORD = "admin" -#db_connection: psycopg.Connection = None class BaseTestCase(TestCase): + temporary_template_db = "openslides_template" + work_on_test_db = "openslides_test" db_connection: psycopg.Connection = None - curs: psycopg.Cursor = None @classmethod - def setup_class(cls): - """setup any state specific to the execution of the given class (which - usually contains tests). - """ - start = datetime.now() + def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:callable = psycopg.rows.dict_row) -> None: env = os.environ try: - cls.db_connection = psycopg.connect(f"dbname='{env['POSTGRES_DB']}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'") + cls.db_connection = psycopg.connect(f"dbname='{db_name}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'", autocommit=autocommit, row_factory=row_factory) except Exception as e: - raise Exception(f"Cannot connect to database: {e.message}") - cls.db_connection.autocommit = False - print(f"class setup: {datetime.now() - start}") + raise Exception(f"Cannot connect to postgres: {e.message}") + @classmethod + def setup_class(cls): + env = os.environ + cls.set_db_connection("postgres", True) + with cls.db_connection: + with cls.db_connection.cursor() as curs: + curs.execute( + sql.SQL("DROP DATABASE IF EXISTS {temporary_template_db} (FORCE);").format( + temporary_template_db=sql.Identifier(cls.temporary_template_db) + ) + ) + curs.execute( + sql.SQL("CREATE DATABASE {db_to_create} TEMPLATE {template_db};").format( + db_to_create=sql.Identifier(cls.temporary_template_db), + template_db=sql.Identifier(env['POSTGRES_DB']))) + cls.set_db_connection(cls.temporary_template_db) + with cls.db_connection: + cls.populate_database() @classmethod def teardown_class(cls): - """teardown any state that was previously setup with a call to - setup_class. - """ - start = datetime.now() - cls.db_connection.close() - print(f"class teardown: {datetime.now() - start}") + """ remove last test db and drop the temporary template db""" + cls.set_db_connection("postgres", True) + with cls.db_connection: + with cls.db_connection.cursor() as curs: + curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(cls.work_on_test_db))) + curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(cls.temporary_template_db))) def setUp(self) -> None: - start = datetime.now() - #global db_connection - super().setUp() - env = os.environ - self.curs = self.db_connection.cursor() - self.curs.execute(f"select truncate_tables('{env['POSTGRES_USER']}');") - self.db_connection.commit() - print(f"test setup: {datetime.now() - start}") - - def tearDown(self) -> None: - start = datetime.now() - super().tearDown() - self.curs.close() - print(f"test teardown: {datetime.now() - start}") - - # self.created_fqids = set() - # self.create_model( - # "user/1", - # { - # "username": ADMIN_USERNAME, - # "password": self.auth.hash(ADMIN_PASSWORD), - # "default_password": ADMIN_PASSWORD, - # "is_active": True, - # "organization_management_level": "superadmin", - # "organization_id": ONE_ORGANIZATION_ID, - # }, - # ) - # self.create_model( - # ONE_ORGANIZATION_FQID, - # { - # "name": "OpenSlides Organization", - # "default_language": "en", - # "user_ids": [1], - # }, - # ) - # self.client = self.create_client(self.update_vote_service_auth_data) - # if self.auth_data: - # # Reuse old login data to avoid a new login request - # self.client.update_auth_data(self.auth_data) - # else: - # # Login and save copy of auth data for all following tests - # self.client.login(ADMIN_USERNAME, ADMIN_PASSWORD) - # BaseSystemTestCase.auth_data = deepcopy(self.client.auth_data) - # self.anon_client = self.create_client() - - - #@pytest.fixture(scope="session", autouse=True) - def setup_db_connect(self): - start = datetime.now() - global db_connection - env = os.environ - try: - db_connection = psycopg.connect(f"dbname='{env['POSTGRES_DB']}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'") - except Exception as e: - raise Exception(f"Cannot connect to database: {e.message}") - db_connection.autocommit = False - print(f"session setup: {datetime.now() - start}") - yield db_connection - start = datetime.now() - db_connection.close() - print(f"session teardown: {datetime.now() - start}") - - # def load_example_data(self) -> None: - # """ - # Useful for debug purposes when an action fails with the example data. - # Do NOT use in final tests since it takes a long time. - # """ - # example_data = get_initial_data_file(EXAMPLE_DATA_FILE) - # self._load_data(example_data) - - # def load_json_data(self, filename: str) -> None: - # """ - # Useful for debug purposes when an action fails with a specific dump. - # Do NOT use in final tests since it takes a long time. - # """ - # with open(filename) as file: - # data = json.loads(file.read()) - # self._load_data(data) - - # def _load_data(self, raw_data: dict[str, dict[str, Any]]) -> None: - # data = {} - # for collection, models in raw_data.items(): - # if collection == "_migration_index": - # continue - # for model_id, model in models.items(): - # data[f"{collection}/{model_id}"] = { - # f: v for f, v in model.items() if not f.startswith("meta_") - # } - # self.set_models(data) - - # def create_client( - # self, on_auth_data_changed: Callable[[AuthData], None] | None = None - # ) -> Client: - # return Client(self.app, on_auth_data_changed) + self.set_db_connection("postgres", autocommit=True) + with self.db_connection: + with self.db_connection.cursor() as curs: + curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(self.work_on_test_db))) + curs.execute(sql.SQL("CREATE DATABASE {test_db} TEMPLATE {temporary_template_db};").format( + test_db=sql.Identifier(self.work_on_test_db), + temporary_template_db=sql.Identifier(self.temporary_template_db))) - # def login(self, user_id: int) -> None: - # """ - # Login the given user by fetching the default password from the datastore. - # """ - # user = self.get_model(f"user/{user_id}") - # assert user.get("default_password") - # self.client.login(user["username"], user["default_password"]) + self.set_db_connection(self.work_on_test_db) - # def update_vote_service_auth_data(self, auth_data: AuthData) -> None: - # self.vote_service.set_authentication( - # auth_data["access_token"], auth_data["refresh_id"] - # ) - - # def get_application(self) -> WSGIApplication: - # raise NotImplementedError() - - # def assert_status_code(self, response: Response, code: int) -> None: - # if ( - # response.status_code != code - # and response.json - # and response.json.get("message") - # ): - # print(response.json) - # self.assertEqual(response.status_code, code) - - # def create_model( - # self, fqid: str, data: dict[str, Any] = {}, deleted: bool = False - # ) -> None: - # write_request = self.get_write_request( - # self.get_create_events(fqid, data, deleted) - # ) - # self.datastore.write(write_request) - - # def update_model(self, fqid: str, data: dict[str, Any]) -> None: - # write_request = self.get_write_request(self.get_update_events(fqid, data)) - # self.datastore.write(write_request) - - # def get_create_events( - # self, fqid: str, data: dict[str, Any] = {}, deleted: bool = False - # ) -> list[Event]: - # self.created_fqids.add(fqid) - # data["id"] = id_from_fqid(fqid) - # self.validate_fields(fqid, data) - # events = [Event(type=EventType.Create, fqid=fqid, fields=data)] - # if deleted: - # events.append(Event(type=EventType.Delete, fqid=fqid)) - # return events - - # def get_update_events(self, fqid: str, data: dict[str, Any]) -> list[Event]: - # self.validate_fields(fqid, data) - # return [Event(type=EventType.Update, fqid=fqid, fields=data)] - - # def get_write_request(self, events: list[Event]) -> WriteRequest: - # return WriteRequest(events, user_id=0) - - # def set_models(self, models: dict[str, dict[str, Any]]) -> None: - # """ - # Can be used to set multiple models at once, independent of create or update. - # Uses self.created_fqids to determine which models are already created. If you want to update - # a model which was not set in the test but created via an action, you may have to add the - # fqid to this set. - # """ - # events: list[Event] = [] - # for fqid, model in models.items(): - # if fqid in self.created_fqids: - # events.extend(self.get_update_events(fqid, model)) - # else: - # events.extend(self.get_create_events(fqid, model)) - # write_request = self.get_write_request(events) - # self.datastore.write(write_request) - - # def validate_fields(self, fqid: str, fields: dict[str, Any]) -> None: - # model = model_registry[collection_from_fqid(fqid)]() - # for field_name, value in fields.items(): - # try: - # model.get_field(field_name).validate_with_schema( - # fqid, field_name, value - # ) - # except ActionException as e: - # raise JsonSchemaException(e.message) - - # @with_database_context - # def get_model(self, fqid: str) -> dict[str, Any]: - # model = self.datastore.get( - # fqid, - # mapped_fields=[], - # get_deleted_models=DeletedModelsBehaviour.ALL_MODELS, - # lock_result=False, - # use_changed_models=False, - # ) - # self.assertTrue(model) - # self.assertEqual(model.get("id"), id_from_fqid(fqid)) - # return model - - # def assert_model_exists( - # self, fqid: str, fields: dict[str, Any] | None = None - # ) -> dict[str, Any]: - # return self._assert_fields(fqid, (fields or {}) | {"meta_deleted": False}) - - # def assert_model_not_exists(self, fqid: str) -> None: - # with self.assertRaises(DatastoreException): - # self.get_model(fqid) - - # def assert_model_deleted( - # self, fqid: str, fields: dict[str, Any] | None = None - # ) -> dict[str, Any]: - # return self._assert_fields(fqid, (fields or {}) | {"meta_deleted": True}) - - # def _assert_fields( - # self, fqid: FullQualifiedId, fields: dict[str, Any] - # ) -> dict[str, Any]: - # model = self.get_model(fqid) - # model_cls = model_registry[collection_from_fqid(fqid)]() - # for field_name, value in fields.items(): - # if not is_reserved_field(field_name) and value is not None: - # # assert that the field actually exists to detect errors in the tests - # model_cls.get_field(field_name) - # self.assertEqual( - # model.get(field_name), - # value, - # f"Models differ in field {field_name}!", - # ) - # return model - - # def assert_defaults(self, model: type[Model], instance: dict[str, Any]) -> None: - # for field in model().get_fields(): - # if getattr(field, "default", None) is not None: - # self.assertEqual( - # field.default, - # instance.get(field.own_field_name), - # f"Field {field.own_field_name}: Value {instance.get(field.own_field_name, 'None')} is not equal default value {field.default}.", - # ) + @classmethod + def populate_database(cls) -> None: + """ do something like setting initial_data.json""" + with cls.db_connection.transaction(): + with cls.db_connection.cursor() as curs: + theme_id = DbUtils.insert_wrapper(curs, "themeT", { + "name": "OpenSlides Blue", + "accent_500": int("0x2196f3", 16), + "primary_500": int("0x317796", 16), + "warn_500": int("0xf06400", 16), + }) + organization_id = DbUtils.insert_wrapper(curs, "organizationT", { + "name": "Test Organization", + "legal_notice": "OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.", + "login_text": "Good Morning!", + "default_language": "en", + "genders": ["male", "female", "diverse", "non-binary"], + "enable_electronic_voting": True, + "enable_chat": True, + "reset_password_verbose_errors": True, + "limit_of_meetings": 0, + "limit_of_users": 0, + "theme_id": theme_id, + "users_email_sender": "OpenSlides", + "users_email_subject": "OpenSlides access data", + "users_email_body": "Dear {name},\n\nthis is your personal OpenSlides login:\n\n{url}\nUsername: {username}\nPassword: {password}\n\n\nThis email was generated automatically.", + "url": "https://example.com", + "saml_enabled": False, + "saml_login_button_text": "SAML Login", + "saml_attr_mapping": Jsonb({ + "saml_id": "username", + "title": "title", + "first_name": "firstName", + "last_name": "lastName", + "email": "email", + "gender": "gender", + "pronoun": "pronoun", + "is_active": "is_active", + "is_physical_person": "is_person" + }) + }) + user_id = DbUtils.insert_wrapper(curs, "userT", { + "username": "admin", + "last_name": "Administrator", + "is_active": True, + "is_physical_person": True, + "password": "316af7b2ddc20ead599c38541fbe87e9a9e4e960d4017d6e59de188b41b2758flD5BVZAZ8jLy4nYW9iomHcnkXWkfk3PgBjeiTSxjGG7+fBjMBxsaS1vIiAMxYh+K38l0gDW4wcP+i8tgoc4UBg==", + "default_password": "admin", + "can_change_own_password": True, + "gender": "male", + "default_vote_weight": "1.000000", + "organization_management_level": "superadmin", + }) + committee_id = DbUtils.insert_wrapper(curs, "committeeT", { + "name": "Default committee", + "description": "Add description here", + }) + meeting_id = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as new_id;").fetchone()["new_id"] + group_ids = DbUtils.insert_many_wrapper(curs, "groupT", [ + { + "name": "Default", + "permissions": [ + "agenda_item.can_see_internal", + "assignment.can_see", + "list_of_speakers.can_see", + "mediafile.can_see", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see", + "user.can_see" + ], + "weight": 1, + "meeting_id": meeting_id + }, + { + "name": "Admin", + "permissions": [], + "weight": 2, + "meeting_id": meeting_id + }, + { + "name": "Staff", + "permissions": [ + "agenda_item.can_manage", + "assignment.can_manage", + "assignment.can_nominate_self", + "list_of_speakers.can_be_speaker", + "list_of_speakers.can_manage", + "mediafile.can_manage", + "meeting.can_see_frontpage", + "meeting.can_see_history", + "motion.can_manage", + "poll.can_manage", + "projector.can_manage", + "tag.can_manage", + "user.can_manage" + ], + "weight": 3, + "meeting_id": meeting_id + }, + { + "name": "Committees", + "permissions": [ + "agenda_item.can_see_internal", + "assignment.can_see", + "list_of_speakers.can_see", + "mediafile.can_see", + "meeting.can_see_frontpage", + "motion.can_create", + "motion.can_create_amendments", + "motion.can_support", + "projector.can_see", + "user.can_see" + ], + "weight": 4, + "meeting_id": meeting_id + }, + { + "name": "Delegates", + "permissions": [ + "agenda_item.can_see_internal", + "assignment.can_nominate_other", + "assignment.can_nominate_self", + "list_of_speakers.can_be_speaker", + "mediafile.can_see", + "meeting.can_see_autopilot", + "meeting.can_see_frontpage", + "motion.can_create", + "motion.can_create_amendments", + "motion.can_support", + "projector.can_see", + "user.can_see" + ], + "weight": 5, + "meeting_id": meeting_id + } + ]) + projector_ids = DbUtils.insert_many_wrapper(curs, "projectorT", [ + { + "name": "Default projector", + "is_internal": False, + "scale": 0, + "scroll": 0, + "width": 1220, + "aspect_ratio_numerator": 4, + "aspect_ratio_denominator": 3, + "color": int("0x000000", 16), + "background_color": int("0xffffff", 16), + "header_background_color": int("0x317796", 16), + "header_font_color": int("0xf5f5f5", 16), + "header_h1_color": int("0x317796", 16), + "chyron_background_color": int("0x317796", 16), + "chyron_font_color": int("0xffffff", 16), + "show_header_footer": True, + "show_title": True, + "show_logo": True, + "show_clock": True, + "sequential_number": 1, + "meeting_id": meeting_id + }, + { + "name": "Nebenprojektor", + "is_internal": False, + "scale": 0, + "scroll": 0, + "width": 1024, + "aspect_ratio_numerator": 16, + "aspect_ratio_denominator": 9, + "color": int("0x000000", 16), + "background_color": int("0x888888", 16), + "header_background_color": int("0x317796", 16), + "header_font_color": int("0xf5f5f5", 16), + "header_h1_color": int("0x317796", 16), + "chyron_background_color": int("0x317796", 16), + "chyron_font_color": int("0xffffff", 16), + "show_header_footer": True, + "show_title": True, + "show_logo": True, + "show_clock": True, + "sequential_number": 2, + "meeting_id": meeting_id + } + ]) + workflow_simple_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + workflow_complex_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + wf_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + { + "name": "submitted", + "weight": 1, + "css_class": "lightblue", + "allow_support": True, + "allow_create_poll": True, + "allow_submitter_edit": True, + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_simple_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "set_workflow_timestamp": True, + "allow_motion_forwarding": True, + "meeting_id": meeting_id + }, + { + "name": "accepted", + "weight": 2, + "recommendation_label": "Acceptance", + "css_class": "green", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_simple_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "set_workflow_timestamp": False, + "allow_motion_forwarding": True, + "meeting_id": meeting_id + }, + { + "name": "rejected", + "weight": 3, + "recommendation_label": "Rejection", + "css_class": "red", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_simple_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "not decided", + "weight": 4, + "recommendation_label": "No decision", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_simple_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + ]) + wf_simple_first_state_id = wf_simple_motion_state_ids[0] + wf_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + { + "name": "in progress", + "weight": 5, + "css_class": "lightblue", + "set_number": False, + "allow_submitter_edit": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_create_poll": False, + "allow_support": False, + "set_workflow_timestamp": True, + "allow_motion_forwarding": True, + "meeting_id": meeting_id + }, + { + "name": "submitted", + "weight": 6, + "css_class": "lightblue", + "set_number": False, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": True, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "permitted", + "weight": 7, + "recommendation_label": "Permission", + "css_class": "lightblue", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": True, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": 1 + }, + { + "name": "accepted", + "weight": 8, + "recommendation_label": "Acceptance", + "css_class": "green", + "set_number": True, + "merge_amendment_into_final": "do_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "rejected", + "weight": 9, + "recommendation_label": "Rejection", + "css_class": "red", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": 2, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "withdrawn", + "weight": 10, + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "adjourned", + "weight": 11, + "recommendation_label": "Adjournment", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "not concerned", + "weight": 12, + "recommendation_label": "No concernment", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "referred to committee", + "weight": 13, + "recommendation_label": "Referral to committee", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "needs review", + "weight": 14, + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": meeting_id + }, + { + "name": "rejected (not authorized)", + "weight": 15, + "recommendation_label": "Rejection (not authorized)", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": workflow_complex_id, + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": 1 + }, + ]) + wf_complex_first_state_id = wf_complex_motion_state_ids[0] + assert [workflow_simple_id, workflow_complex_id] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", + [ + { + "id": workflow_simple_id, + "name": "Simple Workflow", + "sequential_number": 1, + "first_state_id": wf_simple_first_state_id, + "meeting_id": 1 + }, + { + "id": workflow_complex_id, + "name": "Complex Workflow", + "sequential_number": 2, + "first_state_id": wf_complex_first_state_id, + "meeting_id": 1 + } + ] + ) + assert 1 == DbUtils.insert_wrapper(curs, "meetingT", { + "id": meeting_id, + "name": "OpenSlides Demo", + "is_active_in_organization_id": organization_id, + "language": "en", + "conference_los_restriction": True, + "agenda_number_prefix": "TOP", + "motions_default_workflow_id": workflow_simple_id, + "motions_default_amendment_workflow_id": workflow_complex_id, + "motions_default_statute_amendment_workflow_id": workflow_complex_id, + "motions_recommendations_by": "ABK", + "motions_statute_recommendations_by": "", + "motions_statutes_enabled": True, + "motions_amendments_of_amendments": True, + "motions_amendments_prefix": "-\u00c4", + "motions_supporters_min_amount": 1, + "motions_export_preamble": "", + "users_enable_presence_view": True, + "users_pdf_wlan_encryption": "", + "users_enable_vote_delegations": True, + "poll_ballot_paper_selection": "CUSTOM_NUMBER", + "poll_ballot_paper_number": 8, + "poll_sort_poll_result_by_votes": True, + "poll_default_type": "nominal", + "poll_default_method": "votes", + "poll_default_onehundred_percent_base": "valid", + "committee_id": committee_id, + "reference_projector_id": projector_ids[0], + # Fields still not generated, required relation_list not implemented + # "default_projector_agenda_item_list_ids": [projector_ids[0]], + # "default_projector_topic_ids": [projector_ids[0]], + # "default_projector_list_of_speakers_ids": [projector_ids[1]], + # "default_projector_current_list_of_speakers_ids": [projector_ids[1]], + # "default_projector_motion_ids": [projector_ids[0]], + # "default_projector_amendment_ids": [projector_ids[0]], + # "default_projector_motion_block_ids": [projector_ids[0]], + # "default_projector_assignment_ids": [projector_ids[0]], + # "default_projector_mediafile_ids": [projector_ids[0]], + # "default_projector_message_ids": [projector_ids[0]], + # "default_projector_countdown_ids": [projector_ids[0]], + # "default_projector_assignment_poll_ids": [projector_ids[0]], + # "default_projector_motion_poll_ids": [projector_ids[0]], + # "default_projector_poll_ids": [projector_ids[0]], + "default_group_id": group_ids[0], + "admin_group_id": group_ids[1] + }) + curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (meeting_id, committee_id)) + """todo: + workflow anlegen + + """ + print("Transaction committed") - # @with_database_context - # def assert_model_count(self, collection: str, meeting_id: int, count: int) -> None: - # db_count = self.datastore.count( - # collection, - # FilterOperator("meeting_id", "=", meeting_id), - # lock_result=False, - # ) - # self.assertEqual(db_count, count) diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index c6a8a709..9be25ffe 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -4,21 +4,12 @@ from tests.base import BaseTestCase # , db_connection -#@pytest.mark.usefixtures("setup_db_connect") class GenericRelations(BaseTestCase): - def test_1(self) -> None: - print("test1 ohne daten") - assert 0 - def test_2(self) -> None: start = datetime.now() with self.db_connection.transaction(): - for i in range(100): - result = self.curs.execute("insert into themeT (name, accent_500, primary_500, warn_500) VALUES (%s, %s, %s, %s)", (f"name{i}", i, i*10, i*100)) + with self.db_connection.cursor() as curs: + for i in range(100): + curs.execute("insert into themeT (name, accent_500, primary_500, warn_500) VALUES (%s, %s, %s, %s)", (f"name{i}", i, i*10, i*100)) print(f"test2 100 Sätze per Loop: {datetime.now() - start}") - assert 0 - - def test_3(self) -> None: - print("test3 ohne daten") - assert 0 diff --git a/models.yml b/models.yml index 6e827ccb..46a400f1 100644 --- a/models.yml +++ b/models.yml @@ -20,11 +20,7 @@ # relation field in this collection is . E. g. in a motion the field `meeting_id` # `reference: , where the collection is the same as that from the `to`. -# `category_id` links to one category where the field `motion_ids` contains the -# motion id. The simple notation for the field is `motion_category/motion_ids`. -# The reverse field has type `relation-list` and is related back to -# `motion/category_id`. The type indicates that there are many -# motion ids. +# `deferred: true`: this field get the "INITIALLY DEFERRED" on it's foreign key definition # - Generic relations: The difference to non-generic relations is that you have a # list of possible fields, so `to` can either hold multiple collections (if the # field name is the same): @@ -2530,9 +2526,10 @@ motion: to: motion/all_origin_ids restriction_mode: A identical_motion_ids: - type: relation-list - to: motion/identical_motion_ids - equal_fields: meeting_id + type: int[] + # type relation-list + # to: motion/identical_motion_ids + description: Changed from relation-list to int[], because can't still be genmerated restriction_mode: C state_id: type: relation @@ -3127,6 +3124,7 @@ motion_state: required: true restriction_mode: A constant: true + deferred: true motion_workflow: id: From 737d98ff28dfc282db9fd4682aed952dde4e6b69 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 29 Feb 2024 19:45:33 +0100 Subject: [PATCH 019/142] workflow_states defined, common data test-setup in db-template ready --- dev/sql/schema_relational.sql | 9 +++++---- dev/src/db_utils.py | 15 ++++++++------- dev/tests/base.py | 25 +++++++++++++++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 4509987b..d07aaea4 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -14,7 +14,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '665a7daf1eac5b0731787068b449e0b4' +-- MODELS_YML_CHECKSUM = 'e27b9c608b3e6c03917de589385560a2' -- Type definitions DO $$ BEGIN @@ -253,7 +253,7 @@ END$$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_group_permissions') THEN - CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see'); + CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update'); ELSE RAISE NOTICE 'type "enum_group_permissions" already exists, skipping'; END IF; @@ -379,7 +379,7 @@ END$$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_import_preview_name') THEN - CREATE TYPE enum_import_preview_name AS ENUM ('account', 'participant', 'topic', 'committee'); + CREATE TYPE enum_import_preview_name AS ENUM ('account', 'participant', 'topic', 'committee', 'motion'); ELSE RAISE NOTICE 'type "enum_import_preview_name" already exists, skipping'; END IF; @@ -460,7 +460,6 @@ CREATE TABLE IF NOT EXISTS userT ( can_change_own_password boolean DEFAULT True, gender varchar(256), email varchar(256), - default_number varchar(256), default_vote_weight decimal(6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', last_email_sent timestamptz, is_demo_user boolean, @@ -632,7 +631,9 @@ CREATE TABLE IF NOT EXISTS meetingT ( list_of_speakers_show_amount_of_speakers_on_slide boolean DEFAULT True, list_of_speakers_present_users_only boolean DEFAULT False, list_of_speakers_show_first_contribution boolean DEFAULT False, + list_of_speakers_allow_multiple_speakers boolean DEFAULT False, list_of_speakers_enable_point_of_order_speakers boolean DEFAULT True, + list_of_speakers_can_create_point_of_order_for_others boolean DEFAULT False, list_of_speakers_enable_point_of_order_categories boolean DEFAULT False, list_of_speakers_closing_disables_point_of_order boolean DEFAULT False, list_of_speakers_enable_pro_contra_speech boolean DEFAULT False, diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index def70d52..da66fe92 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -18,7 +18,7 @@ def insert_wrapper(cls, curs: Cursor, table_name: str, data: dict[str, Any]) -> @classmethod def insert_many_wrapper( - cls, curs: Cursor, table_name: str, data_list: list[dict[str, Any]] + cls, curs: Cursor, table_name: str, data_list: list[dict[str, Any]], returning: str = "id" ) -> list[int]: ids: list[int] = [] if not data_list: @@ -32,7 +32,7 @@ def insert_many_wrapper( dates = [temp_data | data for data in data_list] query = ( - f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}) RETURNING id;" + f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}){' RETURNING ' + returning if returning else ''};" ) query = ( sql.SQL(query) @@ -44,11 +44,12 @@ def insert_many_wrapper( curs.executemany( query, tuple(tuple(v for _, v in sorted(data.items())) for data in dates), - returning=True, + returning=bool(returning), ) ids = [] - while True: - ids.append(curs.fetchone()["id"]) - if not curs.nextset(): - break + if returning: + while True: + ids.append(curs.fetchone()[returning]) + if not curs.nextset(): + break return ids diff --git a/dev/tests/base.py b/dev/tests/base.py index 7e1415a9..b5224761 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -520,6 +520,25 @@ def populate_database(cls) -> None: }, ]) wf_complex_first_state_id = wf_complex_motion_state_ids[0] + + DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_stateT", + [ + {"next_state_id": 2, "previous_state_id": 1}, + {"next_state_id": 3, "previous_state_id": 1}, + {"next_state_id": 4, "previous_state_id": 1}, + {"next_state_id": 6, "previous_state_id": 5}, + {"next_state_id": 10, "previous_state_id": 5}, + {"next_state_id": 7, "previous_state_id": 6}, + {"next_state_id": 10, "previous_state_id": 6}, + {"next_state_id": 15, "previous_state_id": 6}, + {"next_state_id": 8, "previous_state_id": 7}, + {"next_state_id": 9, "previous_state_id": 7}, + {"next_state_id": 10, "previous_state_id": 7}, + {"next_state_id": 11, "previous_state_id": 7}, + {"next_state_id": 12, "previous_state_id": 7}, + {"next_state_id": 13, "previous_state_id": 7}, + {"next_state_id": 14, "previous_state_id": 7}, + ], returning='') assert [workflow_simple_id, workflow_complex_id] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", [ { @@ -585,9 +604,3 @@ def populate_database(cls) -> None: "admin_group_id": group_ids[1] }) curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (meeting_id, committee_id)) - """todo: - workflow anlegen - - """ - print("Transaction committed") - From 2c35082006ae8f88210f57d6a9c9dd9b7cdc8c93 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 1 Mar 2024 18:41:32 +0100 Subject: [PATCH 020/142] tests for generic and 1:1 relation plus fixes --- dev/sql/schema_relational.sql | 60 +++---- dev/src/db_utils.py | 27 ++- dev/src/generate_sql_schema.py | 6 +- dev/src/helper_get_names.py | 1 + dev/tests/base.py | 150 +++++++++-------- dev/tests/test_generic_relations.py | 248 +++++++++++++++++++++++++++- 6 files changed, 380 insertions(+), 112 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index d07aaea4..e662f9d9 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1566,7 +1566,7 @@ CREATE OR REPLACE VIEW organization AS SELECT *, (select array_agg(m.id) from meetingT m where m.template_for_organization_id = o.id) as template_meeting_ids, (select array_agg(ot.id) from organization_tagT ot) as organization_tag_ids, (select array_agg(t.id) from themeT t) as theme_ids, -(select array_agg(m.owner_id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, (select array_agg(u.id) from userT u) as user_ids FROM organizationT o; @@ -1577,7 +1577,7 @@ CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(c.id) from committeeT c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_userT m where m.user_id = u.id) as meeting_user_ids, (select array_agg(n.poll_id) from nm_poll_voted_ids_userT n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(o.content_object_id) from optionT o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(o.id) from optionT o where o.content_object_id_user_id = u.id) as option_ids, (select array_agg(v.id) from voteT v where v.user_id = u.id) as vote_ids, (select array_agg(v.id) from voteT v where v.delegated_user_id = u.id) as delegated_vote_ids, (select array_agg(p.id) from poll_candidateT p where p.user_id = u.id) as poll_candidate_ids @@ -1614,7 +1614,7 @@ CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(n.user_id) from nm_committee_manager_ids_userT n where n.committee_id = c.id) as manager_ids, (select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, (select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, -(select array_agg(g.tagged_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_committee_id = c.id) as organization_tag_ids +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committeeT c; @@ -1638,7 +1638,7 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(s.id) from speakerT s where s.meeting_id = m.id) as speaker_ids, (select array_agg(t.id) from topicT t where t.meeting_id = m.id) as topic_ids, (select array_agg(g.id) from groupT g where g.meeting_id = m.id) as group_ids, -(select array_agg(m1.owner_id) from mediafileT m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, +(select array_agg(m1.id) from mediafileT m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, (select array_agg(m1.id) from motionT m1 where m1.meeting_id = m.id) as motion_ids, (select array_agg(m1.id) from motionT m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, (select array_agg(mc.id) from motion_comment_sectionT mc where mc.meeting_id = m.id) as motion_comment_section_ids, @@ -1662,9 +1662,9 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, (select array_agg(s.id) from structure_levelT s where s.meeting_id = m.id) as structure_level_ids, (select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, -(select array_agg(g.tagged_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids +(select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids FROM meetingT m; @@ -1695,15 +1695,15 @@ FROM tagT t; CREATE OR REPLACE VIEW agenda_item AS SELECT *, (select array_agg(ai.id) from agenda_itemT ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_agenda_item_id = a.id) as tag_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_agenda_item_id = a.id) as projection_ids +(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(p.id) from projectionT p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_itemT a; CREATE OR REPLACE VIEW list_of_speakers AS SELECT *, (select array_agg(s.id) from speakerT s where s.list_of_speakers_id = l.id) as speaker_ids, (select array_agg(s.id) from structure_level_list_of_speakersT s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids +(select array_agg(p.id) from projectionT p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids FROM list_of_speakersT l; @@ -1718,11 +1718,11 @@ FROM point_of_order_categoryT p; CREATE OR REPLACE VIEW topic AS SELECT *, -(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_topic_id = t.id) as attachment_ids, +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_topic_id = t.id) as attachment_ids, (select a.id from agenda_itemT a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakersT l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, -(select array_agg(p.content_object_id) from pollT p where p.content_object_id_topic_id = t.id) as poll_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_topic_id = t.id) as projection_ids +(select array_agg(p.id) from pollT p where p.content_object_id_topic_id = t.id) as poll_ids, +(select array_agg(p.id) from projectionT p where p.content_object_id_topic_id = t.id) as projection_ids FROM topicT t; @@ -1733,23 +1733,23 @@ CREATE OR REPLACE VIEW motion AS SELECT *, (select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_derived_motion_id = m.id) as all_origin_ids, (select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_origin_id = m.id) as all_derived_motion_ids, (select array_agg(g.id) from gm_motion_state_extension_reference_idsT g where g.motion_id = m.id) as state_extension_reference_ids, -(select array_agg(g.state_extension_reference_id) from gm_motion_state_extension_reference_idsT g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.motion_id) from gm_motion_state_extension_reference_idsT g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, (select array_agg(g.id) from gm_motion_recommendation_extension_reference_idsT g where g.motion_id = m.id) as recommendation_extension_reference_ids, -(select array_agg(g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_idsT g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_idsT g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id) from motion_submitterT ms where ms.motion_id = m.id) as submitter_ids, (select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motionT n where n.motion_id = m.id) as supporter_meeting_user_ids, (select array_agg(me.id) from motion_editorT me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id) from motion_working_group_speakerT mw where mw.motion_id = m.id) as working_group_speaker_ids, -(select array_agg(p.content_object_id) from pollT p where p.content_object_id_motion_id = m.id) as poll_ids, -(select array_agg(o.content_object_id) from optionT o where o.content_object_id_motion_id = m.id) as option_ids, +(select array_agg(p.id) from pollT p where p.content_object_id_motion_id = m.id) as poll_ids, +(select array_agg(o.id) from optionT o where o.content_object_id_motion_id = m.id) as option_ids, (select array_agg(mc.id) from motion_change_recommendationT mc where mc.motion_id = m.id) as change_recommendation_ids, (select array_agg(mc.id) from motion_commentT mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_itemT a where a.content_object_id_motion_id = m.id) as agenda_item_id, (select l.id from list_of_speakersT l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, -(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_motion_id = m.id) as attachment_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_motion_id = m.id) as projection_ids, -(select array_agg(p.content_object_id) from personal_noteT p where p.content_object_id_motion_id = m.id) as personal_note_ids +(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_motion_id = m.id) as attachment_ids, +(select array_agg(p.id) from projectionT p where p.content_object_id_motion_id = m.id) as projection_ids, +(select array_agg(p.id) from personal_noteT p where p.content_object_id_motion_id = m.id) as personal_note_ids FROM motionT m; @@ -1770,7 +1770,7 @@ CREATE OR REPLACE VIEW motion_block AS SELECT *, (select array_agg(m1.id) from motionT m1 where m1.block_id = m.id) as motion_ids, (select a.id from agenda_itemT a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, (select l.id from list_of_speakersT l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_motion_block_id = m.id) as projection_ids +(select array_agg(p.id) from projectionT p where p.content_object_id_motion_block_id = m.id) as projection_ids FROM motion_blockT m; @@ -1801,7 +1801,7 @@ CREATE OR REPLACE VIEW poll AS SELECT *, (select array_agg(o.id) from optionT o where o.poll_id = p.id) as option_ids, (select array_agg(n.user_id) from nm_poll_voted_ids_userT n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id) from nm_group_poll_ids_pollT n where n.poll_id = p.id) as entitled_group_ids, -(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_poll_id = p.id) as projection_ids +(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_poll_id = p.id) as projection_ids FROM pollT p; @@ -1813,12 +1813,12 @@ FROM optionT o; CREATE OR REPLACE VIEW assignment AS SELECT *, (select array_agg(ac.id) from assignment_candidateT ac where ac.assignment_id = a.id) as candidate_ids, -(select array_agg(p.content_object_id) from pollT p where p.content_object_id_assignment_id = a.id) as poll_ids, +(select array_agg(p.id) from pollT p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_itemT ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, (select l.id from list_of_speakersT l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tagged_id) from gm_tag_tagged_idsT g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.attachment_id) from gm_mediafile_attachment_idsT g where g.attachment_id_assignment_id = a.id) as attachment_ids, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_assignment_id = a.id) as projection_ids +(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_assignment_id = a.id) as attachment_ids, +(select array_agg(p.id) from projectionT p where p.content_object_id_assignment_id = a.id) as projection_ids FROM assignmentT a; @@ -1833,7 +1833,7 @@ CREATE OR REPLACE VIEW mediafile AS SELECT *, (select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.mediafile_id = m.id) as access_group_ids, (select array_agg(m1.id) from mediafileT m1 where m1.parent_id = m.id) as child_ids, (select l.id from list_of_speakersT l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, -(select array_agg(p.content_object_id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, +(select array_agg(p.id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, (select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids, (select m1.id from meetingT m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meetingT m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, @@ -1863,12 +1863,12 @@ FROM projectorT p; CREATE OR REPLACE VIEW projector_message AS SELECT *, -(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids +(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_messageT p; CREATE OR REPLACE VIEW projector_countdown AS SELECT *, -(select array_agg(p1.content_object_id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, (select m.id from meetingT m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, (select m.id from meetingT m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdownT p; @@ -2083,7 +2083,7 @@ Field Attributes:Field Attributes opposite side t: "to" defined r: "reference" defined s: sql directive given, but must be generated - s+: sql directive includive sql-statement + s+: sql directive inclusive sql-statement R: Required Model.Field -> Model.Field model.field names diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index da66fe92..382b8d9d 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -18,7 +18,11 @@ def insert_wrapper(cls, curs: Cursor, table_name: str, data: dict[str, Any]) -> @classmethod def insert_many_wrapper( - cls, curs: Cursor, table_name: str, data_list: list[dict[str, Any]], returning: str = "id" + cls, + curs: Cursor, + table_name: str, + data_list: list[dict[str, Any]], + returning: str = "id", ) -> list[int]: ids: list[int] = [] if not data_list: @@ -31,9 +35,7 @@ def insert_many_wrapper( temp_data = {k: None for k in keys} dates = [temp_data | data for data in data_list] - query = ( - f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}){' RETURNING ' + returning if returning else ''};" - ) + query = f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}){' RETURNING ' + returning if returning else ''};" query = ( sql.SQL(query) .format( @@ -53,3 +55,20 @@ def insert_many_wrapper( if not curs.nextset(): break return ids + + @classmethod + def select_id_wrapper( + cls, + curs: Cursor, + table_name: str, + id_: int | None = None, + field_names: list[str] = [], + ) -> dict[str, Any] | list[dict[str, Any]]: + """select with single id or all for fields in list or all fields""" + query = sql.SQL( + f"SELECT {', '.join(field_names) if field_names else '*'} FROM {table_name}{' where id = %s' if id_ else ''}" + ) + if id_: + return curs.execute(query, (id_,)).fetchone() + else: + return curs.execute(query).fetchall() diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 9eb5a20e..1fae8da5 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -346,7 +346,7 @@ def get_relation_list_type( f"_{table_name}_{foreign_table_field.ref_column}" ) foreign_table_name = foreign_table_field.table - foreign_table_ref_column = foreign_table_field.column + foreign_table_ref_column = foreign_table_field.ref_column elif type_ == "relation-list": if own_table_field.table == foreign_table_field.table: """Example: committee.forward_to_committee_ids to committee.receive_forwardings_from_committee_ids""" @@ -367,7 +367,7 @@ def get_relation_list_type( ) elif type_ == "generic-relation-list": own_ref_column = own_table_field.ref_column - foreign_table_ref_column = foreign_table_field.column[:-1] + foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" foreign_table_name = HelperGetNames.get_gm_table_name( foreign_table_field ) @@ -623,7 +623,7 @@ class Helper: t: "to" defined r: "reference" defined s: sql directive given, but must be generated - s+: sql directive includive sql-statement + s+: sql directive inclusive sql-statement R: Required Model.Field -> Model.Field model.field names diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 68385d7e..ab7d859c 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -51,6 +51,7 @@ def get_definitions_from_foreign( class HelperGetNames: MAX_LEN = 63 + @staticmethod def max_length(func: Callable) -> Callable: def wrapper(*args, **kwargs) -> str: # type:ignore name = func(*args, **kwargs) diff --git a/dev/tests/base.py b/dev/tests/base.py index b5224761..4e9b8981 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -17,6 +17,22 @@ class BaseTestCase(TestCase): work_on_test_db = "openslides_test" db_connection: psycopg.Connection = None + # id's of pre loaded rows, see method populate_database + meeting1_id= 0 + theme1_id = 0 + organization_id = 0 + user1_id = 0 + committee1_id = 0 + meeting1_id = 0 + group_m1_ids = [0,0,0,0,0] # default, admin, staff, committees, delegates + projector_m1_ids = [0,0] + workflow_m1_simple_id = 0 + workflow_m1_complex_id = 0 + wf_m1_simple_motion_state_ids = [1, 2, 3, 4] + wf_m1_complex_motion_state_ids = [1, 2, 3, 4] + wf_m1_simple_first_state_id = 0 + wf_m1_complex_first_state_id = 0 + @classmethod def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:callable = psycopg.rows.dict_row) -> None: env = os.environ @@ -69,13 +85,13 @@ def populate_database(cls) -> None: """ do something like setting initial_data.json""" with cls.db_connection.transaction(): with cls.db_connection.cursor() as curs: - theme_id = DbUtils.insert_wrapper(curs, "themeT", { + cls.theme1_id = DbUtils.insert_wrapper(curs, "themeT", { "name": "OpenSlides Blue", "accent_500": int("0x2196f3", 16), "primary_500": int("0x317796", 16), "warn_500": int("0xf06400", 16), }) - organization_id = DbUtils.insert_wrapper(curs, "organizationT", { + cls.organization_id = DbUtils.insert_wrapper(curs, "organizationT", { "name": "Test Organization", "legal_notice": "OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.", "login_text": "Good Morning!", @@ -86,7 +102,7 @@ def populate_database(cls) -> None: "reset_password_verbose_errors": True, "limit_of_meetings": 0, "limit_of_users": 0, - "theme_id": theme_id, + "theme_id": cls.theme1_id, "users_email_sender": "OpenSlides", "users_email_subject": "OpenSlides access data", "users_email_body": "Dear {name},\n\nthis is your personal OpenSlides login:\n\n{url}\nUsername: {username}\nPassword: {password}\n\n\nThis email was generated automatically.", @@ -105,7 +121,7 @@ def populate_database(cls) -> None: "is_physical_person": "is_person" }) }) - user_id = DbUtils.insert_wrapper(curs, "userT", { + cls.user1_id = DbUtils.insert_wrapper(curs, "userT", { "username": "admin", "last_name": "Administrator", "is_active": True, @@ -117,12 +133,12 @@ def populate_database(cls) -> None: "default_vote_weight": "1.000000", "organization_management_level": "superadmin", }) - committee_id = DbUtils.insert_wrapper(curs, "committeeT", { + cls.committee1_id = DbUtils.insert_wrapper(curs, "committeeT", { "name": "Default committee", "description": "Add description here", }) - meeting_id = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as new_id;").fetchone()["new_id"] - group_ids = DbUtils.insert_many_wrapper(curs, "groupT", [ + cls.meeting1_id = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as new_id;").fetchone()["new_id"] + cls.group_m1_ids = DbUtils.insert_many_wrapper(curs, "groupT", [ { "name": "Default", "permissions": [ @@ -136,13 +152,13 @@ def populate_database(cls) -> None: "user.can_see" ], "weight": 1, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "Admin", "permissions": [], "weight": 2, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "Staff", @@ -162,7 +178,7 @@ def populate_database(cls) -> None: "user.can_manage" ], "weight": 3, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "Committees", @@ -179,7 +195,7 @@ def populate_database(cls) -> None: "user.can_see" ], "weight": 4, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "Delegates", @@ -198,10 +214,10 @@ def populate_database(cls) -> None: "user.can_see" ], "weight": 5, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id } ]) - projector_ids = DbUtils.insert_many_wrapper(curs, "projectorT", [ + cls.projector_m1_ids = DbUtils.insert_many_wrapper(curs, "projectorT", [ { "name": "Default projector", "is_internal": False, @@ -222,7 +238,7 @@ def populate_database(cls) -> None: "show_logo": True, "show_clock": True, "sequential_number": 1, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "Nebenprojektor", @@ -244,12 +260,12 @@ def populate_database(cls) -> None: "show_logo": True, "show_clock": True, "sequential_number": 2, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id } ]) - workflow_simple_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - workflow_complex_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - wf_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + cls.workflow_m1_simple_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + cls.workflow_m1_complex_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + cls.wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ { "name": "submitted", "weight": 1, @@ -259,13 +275,13 @@ def populate_database(cls) -> None: "allow_submitter_edit": True, "set_number": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_simple_id, + "workflow_id": cls.workflow_m1_simple_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, "set_workflow_timestamp": True, "allow_motion_forwarding": True, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "accepted", @@ -274,7 +290,7 @@ def populate_database(cls) -> None: "css_class": "green", "set_number": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_simple_id, + "workflow_id": cls.workflow_m1_simple_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -283,7 +299,7 @@ def populate_database(cls) -> None: "allow_support": False, "set_workflow_timestamp": False, "allow_motion_forwarding": True, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "rejected", @@ -292,7 +308,7 @@ def populate_database(cls) -> None: "css_class": "red", "set_number": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_simple_id, + "workflow_id": cls.workflow_m1_simple_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -301,7 +317,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "not decided", @@ -310,7 +326,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_simple_id, + "workflow_id": cls.workflow_m1_simple_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -319,11 +335,11 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, ]) - wf_simple_first_state_id = wf_simple_motion_state_ids[0] - wf_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + cls.wf_m1_simple_first_state_id = cls.wf_m1_simple_motion_state_ids[0] + cls.wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ { "name": "in progress", "weight": 5, @@ -331,7 +347,7 @@ def populate_database(cls) -> None: "set_number": False, "allow_submitter_edit": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -339,7 +355,7 @@ def populate_database(cls) -> None: "allow_support": False, "set_workflow_timestamp": True, "allow_motion_forwarding": True, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "submitted", @@ -347,7 +363,7 @@ def populate_database(cls) -> None: "css_class": "lightblue", "set_number": False, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -356,7 +372,7 @@ def populate_database(cls) -> None: "allow_support": True, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "permitted", @@ -365,7 +381,7 @@ def populate_database(cls) -> None: "css_class": "lightblue", "set_number": True, "merge_amendment_into_final": "undefined", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -383,7 +399,7 @@ def populate_database(cls) -> None: "css_class": "green", "set_number": True, "merge_amendment_into_final": "do_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -392,7 +408,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "rejected", @@ -401,7 +417,7 @@ def populate_database(cls) -> None: "css_class": "red", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": 2, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -410,7 +426,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "withdrawn", @@ -418,7 +434,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -427,7 +443,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "adjourned", @@ -436,7 +452,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -445,7 +461,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "not concerned", @@ -454,7 +470,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -463,7 +479,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "referred to committee", @@ -472,7 +488,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -481,7 +497,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "needs review", @@ -489,7 +505,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -498,7 +514,7 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": meeting_id + "meeting_id": cls.meeting1_id }, { "name": "rejected (not authorized)", @@ -507,7 +523,7 @@ def populate_database(cls) -> None: "css_class": "grey", "set_number": True, "merge_amendment_into_final": "do_not_merge", - "workflow_id": workflow_complex_id, + "workflow_id": cls.workflow_m1_complex_id, "restrictions": [], "show_state_extension_field": False, "show_recommendation_extension_field": False, @@ -516,10 +532,10 @@ def populate_database(cls) -> None: "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": 1 + "meeting_id": cls.meeting1_id }, ]) - wf_complex_first_state_id = wf_complex_motion_state_ids[0] + cls.wf_m1_complex_first_state_id = cls.wf_m1_complex_motion_state_ids[0] DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_stateT", [ @@ -539,34 +555,34 @@ def populate_database(cls) -> None: {"next_state_id": 13, "previous_state_id": 7}, {"next_state_id": 14, "previous_state_id": 7}, ], returning='') - assert [workflow_simple_id, workflow_complex_id] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", + assert [cls.workflow_m1_simple_id, cls.workflow_m1_complex_id] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", [ { - "id": workflow_simple_id, + "id": cls.workflow_m1_simple_id, "name": "Simple Workflow", "sequential_number": 1, - "first_state_id": wf_simple_first_state_id, - "meeting_id": 1 + "first_state_id": cls.wf_m1_simple_first_state_id, + "meeting_id": cls.meeting1_id }, { - "id": workflow_complex_id, + "id": cls.workflow_m1_complex_id, "name": "Complex Workflow", "sequential_number": 2, - "first_state_id": wf_complex_first_state_id, - "meeting_id": 1 + "first_state_id": cls.wf_m1_complex_first_state_id, + "meeting_id": cls.meeting1_id } ] ) assert 1 == DbUtils.insert_wrapper(curs, "meetingT", { - "id": meeting_id, + "id": cls.meeting1_id, "name": "OpenSlides Demo", - "is_active_in_organization_id": organization_id, + "is_active_in_organization_id": cls.organization_id, "language": "en", "conference_los_restriction": True, "agenda_number_prefix": "TOP", - "motions_default_workflow_id": workflow_simple_id, - "motions_default_amendment_workflow_id": workflow_complex_id, - "motions_default_statute_amendment_workflow_id": workflow_complex_id, + "motions_default_workflow_id": cls.workflow_m1_simple_id, + "motions_default_amendment_workflow_id": cls.workflow_m1_complex_id, + "motions_default_statute_amendment_workflow_id": cls.workflow_m1_complex_id, "motions_recommendations_by": "ABK", "motions_statute_recommendations_by": "", "motions_statutes_enabled": True, @@ -583,8 +599,8 @@ def populate_database(cls) -> None: "poll_default_type": "nominal", "poll_default_method": "votes", "poll_default_onehundred_percent_base": "valid", - "committee_id": committee_id, - "reference_projector_id": projector_ids[0], + "committee_id": cls.committee1_id, + "reference_projector_id": cls.projector_m1_ids[0], # Fields still not generated, required relation_list not implemented # "default_projector_agenda_item_list_ids": [projector_ids[0]], # "default_projector_topic_ids": [projector_ids[0]], @@ -600,7 +616,7 @@ def populate_database(cls) -> None: # "default_projector_assignment_poll_ids": [projector_ids[0]], # "default_projector_motion_poll_ids": [projector_ids[0]], # "default_projector_poll_ids": [projector_ids[0]], - "default_group_id": group_ids[0], - "admin_group_id": group_ids[1] + "default_group_id": cls.group_m1_ids[0], + "admin_group_id": cls.group_m1_ids[1] }) - curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (meeting_id, committee_id)) + curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (cls.meeting1_id, cls.committee1_id)) diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 9be25ffe..c088a706 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -1,15 +1,247 @@ from datetime import datetime +import psycopg import pytest -from tests.base import BaseTestCase # , db_connection +from psycopg import sql +from src.db_utils import DbUtils +from tests.base import BaseTestCase -class GenericRelations(BaseTestCase): - def test_2(self) -> None: - start = datetime.now() - with self.db_connection.transaction(): +class Relations(BaseTestCase): + """ + Used symbols in names of test: + 1: cardinality 1 + 1G: cardinality 1 with generic-relation field + n: cardinality n + nG: cardinality n with generic-relation-list field + t: "to" defined + r: "reference" defined + s: sql directive given, but must be generated + s+: sql directive inclusive sql-statement + R: Required + """ + + """ 1:1 relation tests """ + """ + FIELD 1tR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id + FIELD 1tR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id + FIELD 1tR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id + + SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id + FIELD 1r: => committee/default_meeting_id:-> meeting/ + + FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id + FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id + FIELD 1tR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id + """ + def test_one_to_one_pre_populated_1rR_1t(self) -> None: + with self.db_connection.cursor() as curs: + organization_row = DbUtils.select_id_wrapper(curs, "organization", self.organization_id, ["theme_id"]) + assert organization_row["theme_id"] == self.theme1_id + theme_row = DbUtils.select_id_wrapper(curs, "theme", self.theme1_id, ["theme_for_organization_id"]) + assert theme_row["theme_for_organization_id"] == self.organization_id + + def test_one_to_one_pre_populated_1r_1t(self) -> None: + with self.db_connection.cursor() as curs: + committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["default_meeting_id"]) + assert committee_row["default_meeting_id"] == self.meeting1_id + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_meeting_for_committee_id"]) + assert meeting_row["default_meeting_for_committee_id"] == self.committee1_id + + def test_one_to_one_1tR_1t(self) -> None: + with self.db_connection.cursor() as curs: + # Prepopulated + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) + old_default_group_id = meeting_row["default_group_id"] + old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) + assert old_default_group_row["default_group_for_meeting_id"] == self.meeting1_id + # change default group + with self.db_connection.transaction(): + group_delegate_row = curs.execute(sql.SQL("SELECT id, name, meeting_id, default_group_for_meeting_id FROM group_ where name = %s and meeting_id = %s;"), ("Delegates", self.meeting1_id)).fetchone() + assert group_delegate_row["id"] == 5 + assert group_delegate_row["name"] == "Delegates" + assert group_delegate_row["meeting_id"] == self.meeting1_id + assert group_delegate_row["default_group_for_meeting_id"] == None + curs.execute(sql.SQL("UPDATE meetingT SET default_group_id = %s where id = %s;"), (group_delegate_row["id"], self.meeting1_id)) + # assert new and old data + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) + assert meeting_row["default_group_id"] == group_delegate_row["id"] + new_default_group_row = DbUtils.select_id_wrapper(curs, "group_", group_delegate_row["id"], ["default_group_for_meeting_id"]) + assert new_default_group_row["default_group_for_meeting_id"] == self.meeting1_id + old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) + assert old_default_group_row["default_group_for_meeting_id"] == None + + """ 1:n relation tests """ + """ n:m relation tests """ + """ manual sqls tests""" + """ all field type tests """ + + """ generic-relation tests """ + def test_generic_1GT_1tR(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + pcl_id = 2 + option_id = 3 + curs.execute("select setval(pg_get_serial_sequence('poll_candidate_listT', 'id'), %s);", (pcl_id,)) + assert pcl_id == DbUtils.insert_wrapper(curs, "poll_candidate_listT", {"id": pcl_id, "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('optionT', 'id'), %s);", (option_id,)) + assert option_id == DbUtils.insert_wrapper(curs, "optionT", {"id": option_id, "content_object_id": (content_object_id := f"poll_candidate_list/{pcl_id}"), "meeting_id": self.meeting1_id}) + option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_poll_candidate_list_id", "content_object_id_user_id"]) + assert option_row["id"] == option_id + assert option_row["content_object_id"] == content_object_id + assert option_row["content_object_id_poll_candidate_list_id"] == pcl_id + assert option_row["content_object_id_user_id"] == None + + pcl_row = DbUtils.select_id_wrapper(curs, "poll_candidate_list", pcl_id, ["id", "option_id",]) + assert pcl_row["id"] == pcl_id + assert pcl_row["option_id"] == option_id + + def test_generic_1GT_nt(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + option_id = 3 + curs.execute("select setval(pg_get_serial_sequence('poll_candidate_listT', 'id'), %s);", (option_id,)) + option_id == DbUtils.insert_wrapper(curs, "optionT", {"id": option_id, "content_object_id": (content_object_id := f"user/{self.user1_id}"), "meeting_id": self.meeting1_id}) + option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_user_id", "content_object_id_poll_candidate_list_id"]) + assert option_row["id"] == option_id + assert option_row["content_object_id"] == content_object_id + assert option_row["content_object_id_user_id"] == self.user1_id + assert option_row["content_object_id_poll_candidate_list_id"] == None + + user_row = DbUtils.select_id_wrapper(curs, "user_", self.user1_id, ["username", "option_ids",]) + assert user_row["option_ids"] == [option_id] + assert user_row["username"] == "admin" + + def test_generic_1GTR_1t(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + mediafile_id = 2 + los_id = 3 + curs.execute("select setval(pg_get_serial_sequence('mediafileT', 'id'), %s);", (mediafile_id,)) + assert mediafile_id == DbUtils.insert_wrapper(curs, "mediafileT", {"id": mediafile_id, "is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}) + curs.execute("select setval(pg_get_serial_sequence('list_of_speakersT', 'id'), %s);", (los_id,)) + assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakersT", {"id": los_id, "content_object_id": (content_object_id := f"mediafile/{mediafile_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) + los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_mediafile_id", "content_object_id_topic_id"]) + assert los_row["id"] == los_id + assert los_row["content_object_id"] == content_object_id + assert los_row["content_object_id_mediafile_id"] == mediafile_id + assert los_row["content_object_id_topic_id"] == None + + mediafile_row = DbUtils.select_id_wrapper(curs, "mediafile", mediafile_id, ["id", "list_of_speakers_id", "owner_id"]) + assert mediafile_row["id"] == mediafile_id + assert mediafile_row["list_of_speakers_id"] == los_id + + def test_generic_1GTR_1tR(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + assignment_id = 2 + los_id = 3 + curs.execute("select setval(pg_get_serial_sequence('assignmentT', 'id'), %s);", (assignment_id,)) + assert assignment_id == DbUtils.insert_wrapper(curs, "assignmentT", {"id": assignment_id, "title": "I am an assignment", "sequential_number": 42, "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('list_of_speakersT', 'id'), %s);", (los_id,)) + assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakersT", {"id": los_id, "content_object_id": (content_object_id := f"assignment/{assignment_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) + los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_assignment_id", "content_object_id_topic_id"]) + assert los_row["id"] == los_id + assert los_row["content_object_id"] == content_object_id + assert los_row["content_object_id_assignment_id"] == assignment_id + assert los_row["content_object_id_topic_id"] == None + + assignment_row = DbUtils.select_id_wrapper(curs, "assignment", assignment_id, ["id", "list_of_speakers_id"]) + assert assignment_row["id"] == assignment_id + assert assignment_row["list_of_speakers_id"] == los_id + + def test_generic_1GTR_nt(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + DbUtils.insert_many_wrapper(curs, "mediafileT", [ + { + "is_public": True, + "owner_id": f"meeting/{self.meeting1_id}" + }, + { + "is_public": True, + "owner_id": f"organization/{self.organization_id}" + }, + { + "is_public": True, + "owner_id": f"meeting/{self.meeting1_id}" + }, + { + "is_public": True, + "owner_id": f"organization/{self.organization_id}" + }, + ]) + rows = DbUtils.select_id_wrapper(curs, "mediafile", field_names=["owner_id", "owner_id_meeting_id", "owner_id_organization_id"]) + expected_results = (("meeting/1", 1, None), ("organization/1", None, 1), ("meeting/1", 1, None), ("organization/1", None, 1)) + for i, row in enumerate (rows): + assert tuple(row.values()) == expected_results[i] + + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["mediafile_ids"]) + assert meeting_row["mediafile_ids"] == [1, 3] + organization_row = DbUtils.select_id_wrapper(curs, "organization", self.organization_id, ["mediafile_ids"]) + assert organization_row["mediafile_ids"] == [2, 4] + + def test_generic_1Gt_check_constraint_error(self) -> None: + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + DbUtils.insert_wrapper(curs, "mediafileT", { + "is_public": True, + "owner_id": f"motion_state/{self.meeting1_id}" + }) + assert 'motion_state/1' in str(e) + + """ generic-relation-list tests """ + def test_generic_nGt_nt(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + tag_ids = DbUtils.insert_many_wrapper(curs, "organization_tagT", [ + { + "name": "Orga Tag 1", + "color": 0xffee13 + }, + { + "name": "Orga Tag 2", + "color": 0x12ee13 + }, + { + "name": "Orga Tag 3", + "color": 0x00ee13 + }, + ]) + DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_idsT", [ + {"organization_tag_id": tag_ids[0], "tagged_id": f"committee/{self.committee1_id}"}, + {"organization_tag_id": tag_ids[0], "tagged_id": f"meeting/{self.meeting1_id}"}, + {"organization_tag_id": tag_ids[1], "tagged_id": f"committee/{self.committee1_id}"}, + {"organization_tag_id": tag_ids[2], "tagged_id": f"meeting/{self.meeting1_id}"}, + ]) + rows = DbUtils.select_id_wrapper(curs, "gm_organization_tag_tagged_idsT", field_names=["id", "organization_tag_id", "tagged_id", "tagged_id_committee_id", "tagged_id_meeting_id"]) + expected_results = ((1, 1, "committee/1", 1, None), (2, 1, "meeting/1", None, 1), (3, 2, "committee/1", 1, None), (4, 3, "meeting/1", None, 1)) + for i, row in enumerate (rows): + assert tuple(row.values()) == expected_results[i] + + committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["organization_tag_ids"]) + assert committee_row["organization_tag_ids"] == [1, 2] + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["organization_tag_ids"]) + assert meeting_row["organization_tag_ids"] == [1, 3] + + def test_generic_nGt_check_constraint_error(self) -> None: + with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.cursor() as curs: - for i in range(100): - curs.execute("insert into themeT (name, accent_500, primary_500, warn_500) VALUES (%s, %s, %s, %s)", (f"name{i}", i, i*10, i*100)) - print(f"test2 100 Sätze per Loop: {datetime.now() - start}") + with self.db_connection.transaction(): + tag_id = DbUtils.insert_wrapper(curs, "organization_tagT", {"name": "Orga Tag 1", "color": 0xffee13}) + DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_idsT", [ + {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}, + {"organization_tag_id": tag_id, "tagged_id": f"motion_state/{self.meeting1_id}"}, + ]) + assert 'motion_state/1' in str(e) + def test_generic_nGt_unique_constraint_error(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + tag_id = DbUtils.insert_wrapper(curs, "organization_tagT", {"name": "Orga Tag 1", "color": 0xffee13}) + DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_idsT", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.transaction(): + DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_idsT", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) + assert 'duplicate key value violates unique constraint' in str(e) From 0839b27a4fb2f05c8ec198070460cc35ce94d17a Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 5 Mar 2024 09:22:55 +0100 Subject: [PATCH 021/142] create_meeting-method --- dev/tests/base.py | 927 ++++++++++++++-------------- dev/tests/test_generic_relations.py | 31 +- 2 files changed, 470 insertions(+), 488 deletions(-) diff --git a/dev/tests/base.py b/dev/tests/base.py index 4e9b8981..31dbd9d4 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -23,15 +23,11 @@ class BaseTestCase(TestCase): organization_id = 0 user1_id = 0 committee1_id = 0 - meeting1_id = 0 - group_m1_ids = [0,0,0,0,0] # default, admin, staff, committees, delegates - projector_m1_ids = [0,0] - workflow_m1_simple_id = 0 - workflow_m1_complex_id = 0 - wf_m1_simple_motion_state_ids = [1, 2, 3, 4] - wf_m1_complex_motion_state_ids = [1, 2, 3, 4] - wf_m1_simple_first_state_id = 0 - wf_m1_complex_first_state_id = 0 + groupM1_default_id = 0 + groupM1_admin_id = 0 + groupM1_staff_id = 0 + simple_workflowM1_id = 0 + complex_workflowM1_id = 0 @classmethod def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:callable = psycopg.rows.dict_row) -> None: @@ -137,452 +133,447 @@ def populate_database(cls) -> None: "name": "Default committee", "description": "Add description here", }) - cls.meeting1_id = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as new_id;").fetchone()["new_id"] - cls.group_m1_ids = DbUtils.insert_many_wrapper(curs, "groupT", [ - { - "name": "Default", - "permissions": [ - "agenda_item.can_see_internal", - "assignment.can_see", - "list_of_speakers.can_see", - "mediafile.can_see", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see", - "user.can_see" - ], - "weight": 1, - "meeting_id": cls.meeting1_id - }, - { - "name": "Admin", - "permissions": [], - "weight": 2, - "meeting_id": cls.meeting1_id - }, - { - "name": "Staff", - "permissions": [ - "agenda_item.can_manage", - "assignment.can_manage", - "assignment.can_nominate_self", - "list_of_speakers.can_be_speaker", - "list_of_speakers.can_manage", - "mediafile.can_manage", - "meeting.can_see_frontpage", - "meeting.can_see_history", - "motion.can_manage", - "poll.can_manage", - "projector.can_manage", - "tag.can_manage", - "user.can_manage" - ], - "weight": 3, - "meeting_id": cls.meeting1_id - }, - { - "name": "Committees", - "permissions": [ - "agenda_item.can_see_internal", - "assignment.can_see", - "list_of_speakers.can_see", - "mediafile.can_see", - "meeting.can_see_frontpage", - "motion.can_create", - "motion.can_create_amendments", - "motion.can_support", - "projector.can_see", - "user.can_see" - ], - "weight": 4, - "meeting_id": cls.meeting1_id - }, - { - "name": "Delegates", - "permissions": [ - "agenda_item.can_see_internal", - "assignment.can_nominate_other", - "assignment.can_nominate_self", - "list_of_speakers.can_be_speaker", - "mediafile.can_see", - "meeting.can_see_autopilot", - "meeting.can_see_frontpage", - "motion.can_create", - "motion.can_create_amendments", - "motion.can_support", - "projector.can_see", - "user.can_see" - ], - "weight": 5, - "meeting_id": cls.meeting1_id - } - ]) - cls.projector_m1_ids = DbUtils.insert_many_wrapper(curs, "projectorT", [ - { - "name": "Default projector", - "is_internal": False, - "scale": 0, - "scroll": 0, - "width": 1220, - "aspect_ratio_numerator": 4, - "aspect_ratio_denominator": 3, - "color": int("0x000000", 16), - "background_color": int("0xffffff", 16), - "header_background_color": int("0x317796", 16), - "header_font_color": int("0xf5f5f5", 16), - "header_h1_color": int("0x317796", 16), - "chyron_background_color": int("0x317796", 16), - "chyron_font_color": int("0xffffff", 16), - "show_header_footer": True, - "show_title": True, - "show_logo": True, - "show_clock": True, - "sequential_number": 1, - "meeting_id": cls.meeting1_id - }, - { - "name": "Nebenprojektor", - "is_internal": False, - "scale": 0, - "scroll": 0, - "width": 1024, - "aspect_ratio_numerator": 16, - "aspect_ratio_denominator": 9, - "color": int("0x000000", 16), - "background_color": int("0x888888", 16), - "header_background_color": int("0x317796", 16), - "header_font_color": int("0xf5f5f5", 16), - "header_h1_color": int("0x317796", 16), - "chyron_background_color": int("0x317796", 16), - "chyron_font_color": int("0xffffff", 16), - "show_header_footer": True, - "show_title": True, - "show_logo": True, - "show_clock": True, - "sequential_number": 2, - "meeting_id": cls.meeting1_id - } - ]) - cls.workflow_m1_simple_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - cls.workflow_m1_complex_id = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - cls.wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ - { - "name": "submitted", - "weight": 1, - "css_class": "lightblue", - "allow_support": True, - "allow_create_poll": True, - "allow_submitter_edit": True, - "set_number": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_simple_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "set_workflow_timestamp": True, - "allow_motion_forwarding": True, - "meeting_id": cls.meeting1_id - }, - { - "name": "accepted", - "weight": 2, - "recommendation_label": "Acceptance", - "css_class": "green", - "set_number": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_simple_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "set_workflow_timestamp": False, - "allow_motion_forwarding": True, - "meeting_id": cls.meeting1_id - }, - { - "name": "rejected", - "weight": 3, - "recommendation_label": "Rejection", - "css_class": "red", - "set_number": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_simple_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "not decided", - "weight": 4, - "recommendation_label": "No decision", - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_simple_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - ]) - cls.wf_m1_simple_first_state_id = cls.wf_m1_simple_motion_state_ids[0] - cls.wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ - { - "name": "in progress", - "weight": 5, - "css_class": "lightblue", - "set_number": False, - "allow_submitter_edit": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_create_poll": False, - "allow_support": False, - "set_workflow_timestamp": True, - "allow_motion_forwarding": True, - "meeting_id": cls.meeting1_id - }, - { - "name": "submitted", - "weight": 6, - "css_class": "lightblue", - "set_number": False, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": True, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "permitted", - "weight": 7, - "recommendation_label": "Permission", - "css_class": "lightblue", - "set_number": True, - "merge_amendment_into_final": "undefined", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": True, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": 1 - }, - { - "name": "accepted", - "weight": 8, - "recommendation_label": "Acceptance", - "css_class": "green", - "set_number": True, - "merge_amendment_into_final": "do_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "rejected", - "weight": 9, - "recommendation_label": "Rejection", - "css_class": "red", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "withdrawn", - "weight": 10, - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "adjourned", - "weight": 11, - "recommendation_label": "Adjournment", - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "not concerned", - "weight": 12, - "recommendation_label": "No concernment", - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "referred to committee", - "weight": 13, - "recommendation_label": "Referral to committee", - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "needs review", - "weight": 14, - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - { - "name": "rejected (not authorized)", - "weight": 15, - "recommendation_label": "Rejection (not authorized)", - "css_class": "grey", - "set_number": True, - "merge_amendment_into_final": "do_not_merge", - "workflow_id": cls.workflow_m1_complex_id, - "restrictions": [], - "show_state_extension_field": False, - "show_recommendation_extension_field": False, - "allow_submitter_edit": False, - "allow_create_poll": False, - "allow_support": False, - "allow_motion_forwarding": True, - "set_workflow_timestamp": False, - "meeting_id": cls.meeting1_id - }, - ]) - cls.wf_m1_complex_first_state_id = cls.wf_m1_complex_motion_state_ids[0] + result_ids = cls.create_meeting(curs, committee_id=cls.committee1_id) + cls.meeting1_id = result_ids["meeting_id"] + cls.groupM1_default_id = result_ids["default_group_id"] + cls.groupM1_admin_id = result_ids["admin_group_id"] + cls.groupM1_staff_id = result_ids["staff_group_id"] + cls.simple_workflowM1_id = result_ids["simple_workflow_id"] + cls.complex_workflowM1_id = result_ids["complex_workflow_id"] + curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (result_ids["meeting_id"], cls.committee1_id)) - DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_stateT", - [ - {"next_state_id": 2, "previous_state_id": 1}, - {"next_state_id": 3, "previous_state_id": 1}, - {"next_state_id": 4, "previous_state_id": 1}, - {"next_state_id": 6, "previous_state_id": 5}, - {"next_state_id": 10, "previous_state_id": 5}, - {"next_state_id": 7, "previous_state_id": 6}, - {"next_state_id": 10, "previous_state_id": 6}, - {"next_state_id": 15, "previous_state_id": 6}, - {"next_state_id": 8, "previous_state_id": 7}, - {"next_state_id": 9, "previous_state_id": 7}, - {"next_state_id": 10, "previous_state_id": 7}, - {"next_state_id": 11, "previous_state_id": 7}, - {"next_state_id": 12, "previous_state_id": 7}, - {"next_state_id": 13, "previous_state_id": 7}, - {"next_state_id": 14, "previous_state_id": 7}, - ], returning='') - assert [cls.workflow_m1_simple_id, cls.workflow_m1_complex_id] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", - [ - { - "id": cls.workflow_m1_simple_id, - "name": "Simple Workflow", - "sequential_number": 1, - "first_state_id": cls.wf_m1_simple_first_state_id, - "meeting_id": cls.meeting1_id - }, - { - "id": cls.workflow_m1_complex_id, - "name": "Complex Workflow", - "sequential_number": 2, - "first_state_id": cls.wf_m1_complex_first_state_id, - "meeting_id": cls.meeting1_id - } - ] - ) - assert 1 == DbUtils.insert_wrapper(curs, "meetingT", { - "id": cls.meeting1_id, + @classmethod + def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int = 0, ) -> None: + """ + Creates meeting with next availale id if not set or id set (lower ids can't be choosed afterwards) + The committee_id must be given and the committee must exist. The meeting will not be set as default meeting of the committee + 3 groups with permissions (Default, Admin, Staff) were created + 2 projectors (Default projector, Secondary projector) were created + 2 workflows (simple, complex were created) + Return Value dict of ids with keys + - meeting_id + - default_group_id, admin_group_id, staff_group_id + - default_project_id, secondary_projector_id + - simple_workflow_id, complex_workflow_id + """ + result = {} + if meeting_id: + sequence_name = curs.execute("select * from pg_get_serial_sequence('meetingT', 'id');").fetchone()["pg_get_serial_sequence"] + last_value = curs.execute(f"select last_value from {sequence_name};").fetchone()["last_value"] + if last_value >= meeting_id: + raise ValueError(f"meeting_id {meeting_id} is not available, last_value in sequence {sequence_name} is {last_value}") + result["meeting_id"] = curs.execute("select setval(pg_get_serial_sequence('meetingT', 'id'), %s);", (meeting_id,)).fetchone()["setval"] + else: + result["meeting_id"] = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as id_;").fetchone()["id_"] + (result["default_group_id"], result["admin_group_id"], result["staff_group_id"]) = DbUtils.insert_many_wrapper(curs, "groupT", [ + { + "name": "Default", + "permissions": [ + "agenda_item.can_see_internal", + "assignment.can_see", + "list_of_speakers.can_see", + "mediafile.can_see", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see", + "user.can_see" + ], + "weight": 1, + "meeting_id": result["meeting_id"] + }, + { + "name": "Admin", + "permissions": [], + "weight": 2, + "meeting_id": result["meeting_id"] + }, + { + "name": "Staff", + "permissions": [ + "agenda_item.can_manage", + "assignment.can_manage", + "assignment.can_nominate_self", + "list_of_speakers.can_be_speaker", + "list_of_speakers.can_manage", + "mediafile.can_manage", + "meeting.can_see_frontpage", + "meeting.can_see_history", + "motion.can_manage", + "poll.can_manage", + "projector.can_manage", + "tag.can_manage", + "user.can_manage" + ], + "weight": 3, + "meeting_id": result["meeting_id"] + } + ]) + (result["default_projector_id"], result["secondary_projector_id"]) = DbUtils.insert_many_wrapper(curs, "projectorT", [ + { + "name": "Default projector", + "is_internal": False, + "scale": 0, + "scroll": 0, + "width": 1220, + "aspect_ratio_numerator": 4, + "aspect_ratio_denominator": 3, + "color": int("0x000000", 16), + "background_color": int("0xffffff", 16), + "header_background_color": int("0x317796", 16), + "header_font_color": int("0xf5f5f5", 16), + "header_h1_color": int("0x317796", 16), + "chyron_background_color": int("0x317796", 16), + "chyron_font_color": int("0xffffff", 16), + "show_header_footer": True, + "show_title": True, + "show_logo": True, + "show_clock": True, + "sequential_number": 1, + "meeting_id": result["meeting_id"] + }, + { + "name": "Secondary projector", + "is_internal": False, + "scale": 0, + "scroll": 0, + "width": 1024, + "aspect_ratio_numerator": 16, + "aspect_ratio_denominator": 9, + "color": int("0x000000", 16), + "background_color": int("0x888888", 16), + "header_background_color": int("0x317796", 16), + "header_font_color": int("0xf5f5f5", 16), + "header_h1_color": int("0x317796", 16), + "chyron_background_color": int("0x317796", 16), + "chyron_font_color": int("0xffffff", 16), + "show_header_footer": True, + "show_title": True, + "show_logo": True, + "show_clock": True, + "sequential_number": 2, + "meeting_id": result["meeting_id"] + } + ]) + result["simple_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + result["complex_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] + wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + { + "name": "submitted", + "weight": 1, + "css_class": "lightblue", + "allow_support": True, + "allow_create_poll": True, + "allow_submitter_edit": True, + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["simple_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "set_workflow_timestamp": True, + "allow_motion_forwarding": True, + "meeting_id": result["meeting_id"] + }, + { + "name": "accepted", + "weight": 2, + "recommendation_label": "Acceptance", + "css_class": "green", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["simple_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "set_workflow_timestamp": False, + "allow_motion_forwarding": True, + "meeting_id": result["meeting_id"] + }, + { + "name": "rejected", + "weight": 3, + "recommendation_label": "Rejection", + "css_class": "red", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["simple_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "not decided", + "weight": 4, + "recommendation_label": "No decision", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["simple_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + ]) + wf_m1_simple_first_state_id = wf_m1_simple_motion_state_ids[0] + wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + { + "name": "in progress", + "weight": 5, + "css_class": "lightblue", + "set_number": False, + "allow_submitter_edit": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_create_poll": False, + "allow_support": False, + "set_workflow_timestamp": True, + "allow_motion_forwarding": True, + "meeting_id": result["meeting_id"] + }, + { + "name": "submitted", + "weight": 6, + "css_class": "lightblue", + "set_number": False, + "merge_amendment_into_final": "undefined", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": True, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "permitted", + "weight": 7, + "recommendation_label": "Permission", + "css_class": "lightblue", + "set_number": True, + "merge_amendment_into_final": "undefined", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": True, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": 1 + }, + { + "name": "accepted", + "weight": 8, + "recommendation_label": "Acceptance", + "css_class": "green", + "set_number": True, + "merge_amendment_into_final": "do_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "rejected", + "weight": 9, + "recommendation_label": "Rejection", + "css_class": "red", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "withdrawn", + "weight": 10, + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "adjourned", + "weight": 11, + "recommendation_label": "Adjournment", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "not concerned", + "weight": 12, + "recommendation_label": "No concernment", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "referred to committee", + "weight": 13, + "recommendation_label": "Referral to committee", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "needs review", + "weight": 14, + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + { + "name": "rejected (not authorized)", + "weight": 15, + "recommendation_label": "Rejection (not authorized)", + "css_class": "grey", + "set_number": True, + "merge_amendment_into_final": "do_not_merge", + "workflow_id": result["complex_workflow_id"], + "restrictions": [], + "show_state_extension_field": False, + "show_recommendation_extension_field": False, + "allow_submitter_edit": False, + "allow_create_poll": False, + "allow_support": False, + "allow_motion_forwarding": True, + "set_workflow_timestamp": False, + "meeting_id": result["meeting_id"] + }, + ]) + wf_m1_complex_first_state_id = wf_m1_complex_motion_state_ids[0] + + DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_stateT", + [ + {"next_state_id": wf_m1_simple_motion_state_ids[1], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, + {"next_state_id": wf_m1_simple_motion_state_ids[2], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, + {"next_state_id": wf_m1_simple_motion_state_ids[3], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, + {"next_state_id": wf_m1_complex_motion_state_ids[1], "previous_state_id": wf_m1_complex_motion_state_ids[0]}, + {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[0]}, + {"next_state_id": wf_m1_complex_motion_state_ids[2], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, + {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, + {"next_state_id": wf_m1_complex_motion_state_ids[10], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, + {"next_state_id": wf_m1_complex_motion_state_ids[3], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[4], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[6], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[7], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[8], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + {"next_state_id": wf_m1_complex_motion_state_ids[9], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, + ], returning='') + assert [result["simple_workflow_id"], result["complex_workflow_id"]] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", + [ + { + "id": result["simple_workflow_id"], + "name": "Simple Workflow", + "sequential_number": 1, + "first_state_id": wf_m1_simple_first_state_id, + "meeting_id": result["meeting_id"] + }, + { + "id": result["complex_workflow_id"], + "name": "Complex Workflow", + "sequential_number": 2, + "first_state_id": wf_m1_complex_first_state_id, + "meeting_id": result["meeting_id"] + } + ] + ) + assert result["meeting_id"] == DbUtils.insert_wrapper(curs, "meetingT", { + "id": result["meeting_id"], "name": "OpenSlides Demo", "is_active_in_organization_id": cls.organization_id, "language": "en", "conference_los_restriction": True, "agenda_number_prefix": "TOP", - "motions_default_workflow_id": cls.workflow_m1_simple_id, - "motions_default_amendment_workflow_id": cls.workflow_m1_complex_id, - "motions_default_statute_amendment_workflow_id": cls.workflow_m1_complex_id, + "motions_default_workflow_id": result["simple_workflow_id"], + "motions_default_amendment_workflow_id": result["complex_workflow_id"], + "motions_default_statute_amendment_workflow_id": result["complex_workflow_id"], "motions_recommendations_by": "ABK", "motions_statute_recommendations_by": "", "motions_statutes_enabled": True, @@ -599,24 +590,24 @@ def populate_database(cls) -> None: "poll_default_type": "nominal", "poll_default_method": "votes", "poll_default_onehundred_percent_base": "valid", - "committee_id": cls.committee1_id, - "reference_projector_id": cls.projector_m1_ids[0], + "committee_id": committee_id, + "reference_projector_id": result["default_projector_id"], # Fields still not generated, required relation_list not implemented - # "default_projector_agenda_item_list_ids": [projector_ids[0]], - # "default_projector_topic_ids": [projector_ids[0]], - # "default_projector_list_of_speakers_ids": [projector_ids[1]], - # "default_projector_current_list_of_speakers_ids": [projector_ids[1]], - # "default_projector_motion_ids": [projector_ids[0]], - # "default_projector_amendment_ids": [projector_ids[0]], - # "default_projector_motion_block_ids": [projector_ids[0]], - # "default_projector_assignment_ids": [projector_ids[0]], - # "default_projector_mediafile_ids": [projector_ids[0]], - # "default_projector_message_ids": [projector_ids[0]], - # "default_projector_countdown_ids": [projector_ids[0]], - # "default_projector_assignment_poll_ids": [projector_ids[0]], - # "default_projector_motion_poll_ids": [projector_ids[0]], - # "default_projector_poll_ids": [projector_ids[0]], - "default_group_id": cls.group_m1_ids[0], - "admin_group_id": cls.group_m1_ids[1] + # "default_projector_agenda_item_list_ids": [result["default_projector_id"]], + # "default_projector_topic_ids": [result["default_projector_id"]], + # "default_projector_list_of_speakers_ids": [result["secondary_projector_id"]], + # "default_projector_current_list_of_speakers_ids": [result["secondary_projector_id"]], + # "default_projector_motion_ids": [result["default_projector_id"]], + # "default_projector_amendment_ids": [result["default_projector_id"]], + # "default_projector_motion_block_ids": [result["default_projector_id"]], + # "default_projector_assignment_ids": [result["default_projector_id"]], + # "default_projector_mediafile_ids": [result["default_projector_id"]], + # "default_projector_message_ids": [result["default_projector_id"]], + # "default_projector_countdown_ids": [result["default_projector_id"]], + # "default_projector_assignment_poll_ids": [result["default_projector_id"]], + # "default_projector_motion_poll_ids": [result["default_projector_id"]], + # "default_projector_poll_ids": [result["default_projector_id"]], + "default_group_id": result["default_group_id"], + "admin_group_id": result["admin_group_id"] }) - curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (cls.meeting1_id, cls.committee1_id)) + return result \ No newline at end of file diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index c088a706..8e61a168 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -22,18 +22,6 @@ class Relations(BaseTestCase): """ """ 1:1 relation tests """ - """ - FIELD 1tR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id - FIELD 1tR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id - FIELD 1tR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id - - SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id - FIELD 1r: => committee/default_meeting_id:-> meeting/ - - FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id - FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id - FIELD 1tR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id - """ def test_one_to_one_pre_populated_1rR_1t(self) -> None: with self.db_connection.cursor() as curs: organization_row = DbUtils.select_id_wrapper(curs, "organization", self.organization_id, ["theme_id"]) @@ -57,24 +45,27 @@ def test_one_to_one_1tR_1t(self) -> None: assert old_default_group_row["default_group_for_meeting_id"] == self.meeting1_id # change default group with self.db_connection.transaction(): - group_delegate_row = curs.execute(sql.SQL("SELECT id, name, meeting_id, default_group_for_meeting_id FROM group_ where name = %s and meeting_id = %s;"), ("Delegates", self.meeting1_id)).fetchone() - assert group_delegate_row["id"] == 5 - assert group_delegate_row["name"] == "Delegates" - assert group_delegate_row["meeting_id"] == self.meeting1_id - assert group_delegate_row["default_group_for_meeting_id"] == None - curs.execute(sql.SQL("UPDATE meetingT SET default_group_id = %s where id = %s;"), (group_delegate_row["id"], self.meeting1_id)) + group_staff_row = curs.execute(sql.SQL("SELECT id, name, meeting_id, default_group_for_meeting_id FROM group_ where name = %s and meeting_id = %s;"), ("Staff", self.meeting1_id)).fetchone() + assert group_staff_row["id"] == self.groupM1_staff_id + assert group_staff_row["name"] == "Staff" + assert group_staff_row["meeting_id"] == self.meeting1_id + assert group_staff_row["default_group_for_meeting_id"] == None + curs.execute(sql.SQL("UPDATE meetingT SET default_group_id = %s where id = %s;"), (group_staff_row["id"], self.meeting1_id)) # assert new and old data meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) - assert meeting_row["default_group_id"] == group_delegate_row["id"] - new_default_group_row = DbUtils.select_id_wrapper(curs, "group_", group_delegate_row["id"], ["default_group_for_meeting_id"]) + assert meeting_row["default_group_id"] == group_staff_row["id"] + new_default_group_row = DbUtils.select_id_wrapper(curs, "group_", group_staff_row["id"], ["default_group_for_meeting_id"]) assert new_default_group_row["default_group_for_meeting_id"] == self.meeting1_id old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) assert old_default_group_row["default_group_for_meeting_id"] == None """ 1:n relation tests """ + """ Test:motion_state.submitter_withdraw_back_ids: sql okay?""" + """ n:m relation tests """ """ manual sqls tests""" """ all field type tests """ + """ constraint tests """ """ generic-relation tests """ def test_generic_1GT_1tR(self) -> None: From 8116b909aac07b0c64aeff85799a2a97abacc742 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 8 Mar 2024 11:25:36 +0100 Subject: [PATCH 022/142] Install postgres debugger as deb package --- dev/Dockerfile-postgres | 8 ++++++++ dev/docker-compose.yml | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 dev/Dockerfile-postgres diff --git a/dev/Dockerfile-postgres b/dev/Dockerfile-postgres new file mode 100644 index 00000000..0791157f --- /dev/null +++ b/dev/Dockerfile-postgres @@ -0,0 +1,8 @@ +FROM postgres:15 + +# Install the postgresql debugger +RUN apt-get update +RUN apt-get install -y --no-install-recommends apt-utils +RUN apt-get install -y --no-install-recommends postgresql-15-pldebugger + +EXPOSE 5432 \ No newline at end of file diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index 537cfb54..8ca76a3f 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -19,7 +19,9 @@ services: networks: - postgres postgres: - image: postgres:15 + build: + context: . + dockerfile: Dockerfile-postgres environment: - POSTGRES_USER=openslides - POSTGRES_PASSWORD=openslides From baa1c0b5d32395c04a78b8007f5b7340e307ab88 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 8 Mar 2024 11:31:30 +0100 Subject: [PATCH 023/142] Implement 1:n relations, where the n-side is required via trigger --- dev/sql/schema_relational.sql | 257 ++++++++++++++++++++++++---- dev/src/generate_sql_schema.py | 77 ++++++++- dev/src/helper_get_names.py | 24 +++ dev/tests/base.py | 29 ++-- dev/tests/test_generic_relations.py | 52 +++++- 5 files changed, 384 insertions(+), 55 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index e662f9d9..720c3278 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,6 +1,47 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. +CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine + +create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ +-- usage with 3 parameters IN TRIGGER DEFINITION: +-- table_name of field to check, usually a field in a view +-- column_name of field to check +-- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null +DECLARE + table_name TEXT; + column_name TEXT; + foreign_key TEXT; + foreign_id INTEGER; + counted INTEGER; +begin + table_name = TG_ARGV[0]; + column_name = TG_ARGV[1]; + foreign_key = TG_ARGV[2]; + + IF (TG_OP = 'INSERT') THEN + foreign_id := hstore(NEW) -> foreign_key; + IF (foreign_id is NOT NULL) THEN + foreign_id = NULL; -- no need to ask DB + END IF; + ELSIF (TG_OP = 'UPDATE') THEN + foreign_id := hstore(NEW) -> foreign_key; + IF (foreign_id is NULL) THEN + foreign_id = OLD.used_as_default_projector_for_topic_in_meeting_id; + END IF; + ELSIF (TG_OP = 'DELETE') THEN + foreign_id := hstore(OLD) -> foreign_key; + END IF; + + IF (foreign_id IS NOT NULL) THEN + EXECUTE format('SELECT array_length(%I, 1) FROM %I where id = %s', column_name, table_name, foreign_id) INTO counted; + IF (counted is NULL) THEN + RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %.%', TG_NAME, table_name, column_name; + END IF; + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return +end; +$not_null_trigger$ language plpgsql; CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ DECLARE @@ -1300,6 +1341,20 @@ CREATE TABLE IF NOT EXISTS projectorT ( show_logo boolean DEFAULT True, show_clock boolean DEFAULT True, sequential_number integer NOT NULL, + used_as_default_projector_for_agenda_item_list_in_meeting_id integer, + used_as_default_projector_for_topic_in_meeting_id integer, + used_as_default_projector_for_list_of_speakers_in_meeting_id integer, + used_as_default_projector_for_current_los_in_meeting_id integer, + used_as_default_projector_for_motion_in_meeting_id integer, + used_as_default_projector_for_amendment_in_meeting_id integer, + used_as_default_projector_for_motion_block_in_meeting_id integer, + used_as_default_projector_for_assignment_in_meeting_id integer, + used_as_default_projector_for_mediafile_in_meeting_id integer, + used_as_default_projector_for_message_in_meeting_id integer, + used_as_default_projector_for_countdown_in_meeting_id integer, + used_as_default_projector_for_assignment_poll_in_meeting_id integer, + used_as_default_projector_for_motion_poll_in_meeting_id integer, + used_as_default_projector_for_poll_in_meeting_id integer, meeting_id integer NOT NULL ); @@ -1664,7 +1719,21 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids +(select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_list_of_speakers_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_assignment_in_meeting_id = m.id) as default_projector_assignment_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_mediafile_in_meeting_id = m.id) as default_projector_mediafile_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_message_in_meeting_id = m.id) as default_projector_message_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, +(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids FROM meetingT m; @@ -2040,6 +2109,20 @@ ALTER TABLE mediafileT ADD FOREIGN KEY(parent_id) REFERENCES mediafileT(id); ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meetingT(id); ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organizationT(id); +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_list_of_speakers_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_current_los_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_amendment_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_block_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_assignment_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_mediafile_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_message_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_countdown_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_assignment_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; +ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; ALTER TABLE projectorT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; ALTER TABLE projectionT ADD FOREIGN KEY(current_projector_id) REFERENCES projectorT(id); @@ -2068,6 +2151,120 @@ ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_us ALTER TABLE chat_messageT ADD FOREIGN KEY(chat_group_id) REFERENCES chat_groupT(id); ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +-- Create trigger + +-- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projectorT.used_as_default_projector_for_agenda_item_list_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_topic_ids against projectorT.used_as_default_projector_for_topic_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projectorT.used_as_default_projector_for_list_of_speakers_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_current_list_of_speakers_ids against projectorT.used_as_default_projector_for_current_los_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_list_of_speakers_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_motion_ids against projectorT.used_as_default_projector_for_motion_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_amendment_ids against projectorT.used_as_default_projector_for_amendment_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_motion_block_ids against projectorT.used_as_default_projector_for_motion_block_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_assignment_ids against projectorT.used_as_default_projector_for_assignment_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_mediafile_ids against projectorT.used_as_default_projector_for_mediafile_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_message_ids against projectorT.used_as_default_projector_for_message_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_countdown_ids against projectorT.used_as_default_projector_for_countdown_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_assignment_poll_ids against projectorT.used_as_default_projector_for_assignment_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_motion_poll_ids against projectorT.used_as_default_projector_for_motion_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); + + +-- definition trigger not null for meeting.default_projector_poll_ids against projectorT.used_as_default_projector_for_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); + +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projectorT +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); + + /* Relation-list infos Generated: What will be generated for left field @@ -2090,7 +2287,7 @@ Model.Field -> Model.Field */ /* -ns+: => organization/committee_ids:-> - +SQL ns+: => organization/committee_ids:-> - SQL nt:1t => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1t => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id SQL nt:1t => organization/template_meeting_ids:-> meeting/template_for_organization_id @@ -2209,20 +2406,20 @@ FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_pr FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id -*** ntR:1t => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id -*** ntR:1t => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id -*** ntR:1t => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id -*** ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id -*** ntR:1t => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id -*** ntR:1t => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id -*** ntR:1t => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id -*** ntR:1t => meeting/default_projector_assignment_ids:-> projector/used_as_default_projector_for_assignment_in_meeting_id -*** ntR:1t => meeting/default_projector_mediafile_ids:-> projector/used_as_default_projector_for_mediafile_in_meeting_id -*** ntR:1t => meeting/default_projector_message_ids:-> projector/used_as_default_projector_for_message_in_meeting_id -*** ntR:1t => meeting/default_projector_countdown_ids:-> projector/used_as_default_projector_for_countdown_in_meeting_id -*** ntR:1t => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id -*** ntR:1t => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id -*** ntR:1t => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id +SQL ntR:1t => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id +SQL ntR:1t => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id +SQL ntR:1t => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id +SQL ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id +SQL ntR:1t => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id +SQL ntR:1t => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id +SQL ntR:1t => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id +SQL ntR:1t => meeting/default_projector_assignment_ids:-> projector/used_as_default_projector_for_assignment_in_meeting_id +SQL ntR:1t => meeting/default_projector_mediafile_ids:-> projector/used_as_default_projector_for_mediafile_in_meeting_id +SQL ntR:1t => meeting/default_projector_message_ids:-> projector/used_as_default_projector_for_message_in_meeting_id +SQL ntR:1t => meeting/default_projector_countdown_ids:-> projector/used_as_default_projector_for_countdown_in_meeting_id +SQL ntR:1t => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id +SQL ntR:1t => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id +SQL ntR:1t => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id FIELD 1r: => meeting/admin_group_id:-> group/ @@ -2445,20 +2642,20 @@ SQL nt:1t => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1t => projector/preview_projection_ids:-> projection/preview_projector_id SQL nt:1t => projector/history_projection_ids:-> projection/history_projector_id SQL 1t:1tR => projector/used_as_reference_projector_meeting_id:-> meeting/reference_projector_id -*** 1t:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids -*** 1t:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids -*** 1t:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids -*** 1t:ntR => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids -*** 1t:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids -*** 1t:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids -*** 1t:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids -*** 1t:ntR => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/default_projector_assignment_ids -*** 1t:ntR => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/default_projector_mediafile_ids -*** 1t:ntR => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/default_projector_message_ids -*** 1t:ntR => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/default_projector_countdown_ids -*** 1t:ntR => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/default_projector_assignment_poll_ids -*** 1t:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/default_projector_motion_poll_ids -*** 1t:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/default_projector_assignment_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/default_projector_mediafile_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/default_projector_message_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/default_projector_countdown_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/default_projector_assignment_poll_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/default_projector_motion_poll_ids +FIELD 1t:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids FIELD 1tR:nt => projector/meeting_id:-> meeting/projector_ids FIELD 1t:nt => projection/current_projector_id:-> projector/current_projection_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 1fae8da5..4b61ad49 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -23,6 +23,7 @@ class SchemaZoneTexts(TypedDict, total=False): view: str alter_table: str alter_table_final: str + create_trigger: str undecided: str final_info: str @@ -99,6 +100,7 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: table_name_code: str = "" view_name_code: str = "" alter_table_final_code: str = "" + create_trigger_code: str = "" final_info_code: str = "" missing_handled_attributes = [] im_table_code = "" @@ -142,6 +144,8 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: view_name_code += Helper.get_view_body_end(table_name, code) if code := schema_zone_texts["alter_table_final"]: alter_table_final_code += code + "\n" + if code := schema_zone_texts["create_trigger"]: + create_trigger_code += code + "\n" if code := schema_zone_texts["final_info"]: final_info_code += code + "\n" for im_table in cls.intermediate_tables.values(): @@ -155,6 +159,7 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: final_info_code, missing_handled_attributes, im_table_code, + create_trigger_code, ) @classmethod @@ -299,13 +304,6 @@ def get_sql_for_relation_1_1( def get_relation_list_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: - """ - ("relation-list", "relation"): False, - ("relation-list", "relation-list"): "decide_alphabetical", # True if own < foreign.collectionfield - ("relation-list", "generic-relation"): False, - ("relation-list", "generic-relation-list"): False, - ("relation-list", None): "decide_sql", // True or Exception - """ text: SchemaZoneTexts = {} own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_field: TableFieldType = ( @@ -334,6 +332,7 @@ def get_relation_list_type( ) if sql := fdata.get("sql", ""): text["view"] = sql + ",\n" + final_info = "SQL " + final_info else: foreign_table_column = cast(str, foreign_table_field.column) foreign_table_field_ref_id = cast(str, foreign_table_field.ref_column) @@ -391,6 +390,9 @@ def get_relation_list_type( foreign_table_column, foreign_table_ref_column, ) + if own_table_field.field_def.get("required"): + text["create_trigger"] = cls.get_trigger_check_not_null_for_relation_lists( + own_table_field.table, own_table_field.column, foreign_table_field.table, foreign_table_field.column) final_info = "SQL " + final_info text["final_info"] = final_info return text @@ -414,6 +416,21 @@ def get_sql_for_relation_n_1( else: return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter}) as {fname},\n" + @classmethod + def get_trigger_check_not_null_for_relation_lists(cls, own_table:str, own_column:str, foreign_table:str, foreign_column) -> str: + foreign_table_t = HelperGetNames.get_table_name(foreign_table) + return dedent( + f""" + -- definition trigger not null for {own_table}.{own_column} against {foreign_table_t}.{foreign_column} + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {foreign_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', '{foreign_column}'); + + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', '{foreign_column}'); + + """ + ) + @classmethod def get_generic_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str @@ -546,6 +563,47 @@ class Helper: """ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. + CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine + + create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ + -- usage with 3 parameters IN TRIGGER DEFINITION: + -- table_name of field to check, usually a field in a view + -- column_name of field to check + -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null + DECLARE + table_name TEXT; + column_name TEXT; + foreign_key TEXT; + foreign_id INTEGER; + counted INTEGER; + begin + table_name = TG_ARGV[0]; + column_name = TG_ARGV[1]; + foreign_key = TG_ARGV[2]; + + IF (TG_OP = 'INSERT') THEN + foreign_id := hstore(NEW) -> foreign_key; + IF (foreign_id is NOT NULL) THEN + foreign_id = NULL; -- no need to ask DB + END IF; + ELSIF (TG_OP = 'UPDATE') THEN + foreign_id := hstore(NEW) -> foreign_key; + IF (foreign_id is NULL) THEN + foreign_id = OLD.used_as_default_projector_for_topic_in_meeting_id; + END IF; + ELSIF (TG_OP = 'DELETE') THEN + foreign_id := hstore(OLD) -> foreign_key; + END IF; + + IF (foreign_id IS NOT NULL) THEN + EXECUTE format('SELECT array_length(%I, 1) FROM %I where id = %s', column_name, table_name, foreign_id) INTO counted; + IF (counted is NULL) THEN + RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %.%', TG_NAME, table_name, column_name; + END IF; + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return + end; + $not_null_trigger$ language plpgsql; CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ DECLARE @@ -904,8 +962,6 @@ def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: if field["type"] == "relation": result = "1" elif field["type"] == "relation-list": - if field.get("required"): - error = True result = "n" elif field["type"] == "generic-relation": result = "1G" @@ -1190,6 +1246,7 @@ def main() -> None: final_info_code, missing_handled_attributes, im_table_code, + create_trigger_code, ) = GenerateCodeBlocks.generate_the_code() with open(DESTINATION, "w") as dest: dest.write(Helper.FILE_TEMPLATE) @@ -1204,6 +1261,8 @@ def main() -> None: dest.write(view_name_code) dest.write("-- Alter table relations\n") dest.write(alter_table_code) + dest.write("-- Create trigger\n") + dest.write(create_trigger_code) dest.write(Helper.RELATION_LIST_AGENDA) dest.write("/*\n") dest.write(final_info_code) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index ab7d859c..78e4aad6 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -50,6 +50,7 @@ def get_definitions_from_foreign( class HelperGetNames: MAX_LEN = 63 + trigger_unique_list: list[str] = [] @staticmethod def max_length(func: Callable) -> Callable: @@ -158,6 +159,29 @@ def get_minlength_constraint_name( """gets the name of minLength constraint""" return f"minlength_{fname}" + @staticmethod + @max_length + def get_not_null_rel_list_insert_trigger_name( + table_name: str, column_name: str, + ) -> str: + """gets the name of the insert trigger for not null on relation lists""" + name = f"tr_i_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + if name in HelperGetNames.trigger_unique_list: + raise Exception(f"trigger {name} is not unique!") + HelperGetNames.trigger_unique_list.append(name) + return name + + @staticmethod + @max_length + def get_not_null_rel_list_upd_del_trigger_name( + table_name: str, column_name: str, + ) -> str: + """gets the name of the update/delete trigger for not null on relation lists""" + name = f"tr_ud_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + if name in HelperGetNames.trigger_unique_list: + raise Exception(f"trigger {name} is not unique!") + HelperGetNames.trigger_unique_list.append(name) + return name class InternalHelper: MODELS: dict[str, dict[str, Any]] = {} diff --git a/dev/tests/base.py b/dev/tests/base.py index 31dbd9d4..82d12445 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -229,6 +229,20 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "show_logo": True, "show_clock": True, "sequential_number": 1, + "used_as_default_projector_for_agenda_item_list_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_topic_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_list_of_speakers_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_current_los_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_motion_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_amendment_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_motion_block_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_assignment_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_mediafile_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_message_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_countdown_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_assignment_poll_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_motion_poll_in_meeting_id": result["meeting_id"], + "used_as_default_projector_for_poll_in_meeting_id": result["meeting_id"], "meeting_id": result["meeting_id"] }, { @@ -592,21 +606,6 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "poll_default_onehundred_percent_base": "valid", "committee_id": committee_id, "reference_projector_id": result["default_projector_id"], - # Fields still not generated, required relation_list not implemented - # "default_projector_agenda_item_list_ids": [result["default_projector_id"]], - # "default_projector_topic_ids": [result["default_projector_id"]], - # "default_projector_list_of_speakers_ids": [result["secondary_projector_id"]], - # "default_projector_current_list_of_speakers_ids": [result["secondary_projector_id"]], - # "default_projector_motion_ids": [result["default_projector_id"]], - # "default_projector_amendment_ids": [result["default_projector_id"]], - # "default_projector_motion_block_ids": [result["default_projector_id"]], - # "default_projector_assignment_ids": [result["default_projector_id"]], - # "default_projector_mediafile_ids": [result["default_projector_id"]], - # "default_projector_message_ids": [result["default_projector_id"]], - # "default_projector_countdown_ids": [result["default_projector_id"]], - # "default_projector_assignment_poll_ids": [result["default_projector_id"]], - # "default_projector_motion_poll_ids": [result["default_projector_id"]], - # "default_projector_poll_ids": [result["default_projector_id"]], "default_group_id": result["default_group_id"], "admin_group_id": result["admin_group_id"] }) diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 8e61a168..362c3e28 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import cast import psycopg import pytest @@ -59,8 +60,57 @@ def test_one_to_one_1tR_1t(self) -> None: old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) assert old_default_group_row["default_group_for_meeting_id"] == None - """ 1:n relation tests """ + """ 1:n relation tests with n-side NOT NULL """ """ Test:motion_state.submitter_withdraw_back_ids: sql okay?""" + def test_one_to_many_1t_ntR_update_error(self) -> None: + """ update removes default projector => Exception""" + with self.db_connection.cursor() as curs: + with pytest.raises(psycopg.errors.RaiseException) as e: + projector_id = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone()['id'] + with self.db_connection.transaction(): + curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_id,)) + assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) + + def test_one_to_many_1t_ntR_update_okay(self) -> None: + """ Update sets new default projector before 2nd removes old default projector""" + with self.db_connection.cursor() as curs: + projector_ids = curs.execute("SELECT id from projector where meeting_id = %s", (self.meeting1_id,)).fetchall() + with self.db_connection.transaction(): + curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) + curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) + assert projector_ids[1]["id"] == DbUtils.select_id_wrapper(curs, 'meeting', self.meeting1_id, ['default_projector_topic_ids'])['default_projector_topic_ids'][0] + + def test_one_to_many_1t_ntR_update_wrong_update_sequence_error(self) -> None: + """ first update removes the projector from meeting => Exception""" + with self.db_connection.cursor() as curs: + projector_ids = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchall() + with pytest.raises(psycopg.errors.RaiseException) as e: + with self.db_connection.transaction(): + curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) + curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) + assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) + + def test_one_to_many_1t_ntR_delete_error(self) -> None: + """ delete projector from meeting => Exception""" + with self.db_connection.cursor() as curs: + projector_id = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone()["id"] + with pytest.raises(psycopg.errors.RaiseException) as e: + with self.db_connection.transaction(): + curs.execute(sql.SQL("DELETE FROM projector where id = %s;"), (projector_id,)) + assert 'Exception: NOT NULL CONSTRAINT VIOLATED' in str(e) + + def test_one_to_many_1t_ntR_delete_insert_okay(self) -> None: + """ first insert, than delete old default projector from meeting => okay""" + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + projector = curs.execute("SELECT * from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone() + field_list = ["meeting_id", "used_as_default_projector_for_agenda_item_list_in_meeting_id", "used_as_default_projector_for_topic_in_meeting_id", "used_as_default_projector_for_list_of_speakers_in_meeting_id", "used_as_default_projector_for_current_los_in_meeting_id", "used_as_default_projector_for_motion_in_meeting_id", "used_as_default_projector_for_amendment_in_meeting_id", "used_as_default_projector_for_motion_block_in_meeting_id", "used_as_default_projector_for_assignment_in_meeting_id", "used_as_default_projector_for_mediafile_in_meeting_id", "used_as_default_projector_for_message_in_meeting_id", "used_as_default_projector_for_countdown_in_meeting_id", "used_as_default_projector_for_assignment_poll_in_meeting_id", "used_as_default_projector_for_motion_poll_in_meeting_id", "used_as_default_projector_for_poll_in_meeting_id"] + data = {fname: projector[fname] for fname in field_list} + data["sequential_number"] = projector["sequential_number"] + 2 + new_projector_id = DbUtils.insert_wrapper(curs, "projectorT", data) + curs.execute(sql.SQL("UPDATE meetingT SET reference_projector_id = %s where id = %s;"), (new_projector_id, projector["meeting_id"])) + curs.execute(sql.SQL("DELETE FROM projector where id = %s;"), (projector["id"],)) + assert new_projector_id == cast(dict, DbUtils.select_id_wrapper(curs, "meeting", projector["meeting_id"], ["default_projector_topic_ids"]))["default_projector_topic_ids"][0] """ n:m relation tests """ """ manual sqls tests""" From 6456c2434d8b4a5c19d740cc9d3bd56ac10f0b20 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 11 Mar 2024 16:01:53 +0100 Subject: [PATCH 024/142] debug capabilities, some new attributes and 1 dummy --- dev/sql/schema_relational.sql | 6 +++--- dev/src/generate_sql_schema.py | 25 +++++++++++++++---------- dev/tests/base.py | 2 ++ models.yml | 6 +++--- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 720c3278..c444c3ab 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'e27b9c608b3e6c03917de589385560a2' +-- MODELS_YML_CHECKSUM = 'be37c20e734ef040be86fbb8f0d4b665' -- Type definitions DO $$ BEGIN @@ -1004,7 +1004,7 @@ comment on column motionT.sequential_number is 'The (positive) serial number of /* Fields without SQL definition for table motion - identical_motion_ids type:int[] no method defined + identical_motion_ids type:dummy[] Unknown Type */ @@ -2683,4 +2683,4 @@ FIELD 1tR:nt => chat_message/meeting_id:-> meeting/chat_message_ids */ -/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 4b61ad49..98c4e197 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -65,7 +65,7 @@ class GenerateCodeBlocks: ) # Key=Name, data: collected content of table @classmethod - def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: + def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -77,6 +77,7 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: im_table_code: Code for intermediate tables. n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field + create_trigger_code Definitions of triggers """ handled_attributes = { "required", @@ -93,8 +94,9 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: "items", "to", # will be used for creating view-fields, but also replacement for fk-reference to id # "on_delete", # must have other name then the key-value-store one + # "sql" # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? - # "unique", # still to design + # "unique", # TODO: still to design } pre_code: str = "" table_name_code: str = "" @@ -173,14 +175,15 @@ def get_method( ) if fname == "id": type_ = "primary_key" - return (FIELD_TYPES[type_].get("method", "").__get__(cls), type_) - if ( - (type_ := fdata.get("type", "")) - and type_ in FIELD_TYPES - and (method := FIELD_TYPES[type_].get("method")) - ): - return (method.__get__(cls), type_) # returns the callable classmethod - text = "no method defined" if type_ else "Unknown Type" + else: + type_ = fdata.get("type", "") + if type_ in FIELD_TYPES: + if (method := FIELD_TYPES[type_].get("method")): + return (method.__get__(cls), type_) # returns the callable classmethod + else: + text = "no method defined" + else: + text = "Unknown Type" return (f" {fname} type:{fdata.get('type')} {text}\n", type_) @classmethod @@ -1002,6 +1005,8 @@ def check_relation_definitions( foreign_collectionfields = [] for foreign_field in foreign_fields: foreign_c, tmp_error = Helper.get_cardinality(foreign_field.field_def) + if own_c == "1tR" and foreign_c == "1r": + raise Exception(f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1tR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut.") foreigns_c.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) diff --git a/dev/tests/base.py b/dev/tests/base.py index 82d12445..f696a5a6 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -54,6 +54,8 @@ def setup_class(cls): template_db=sql.Identifier(env['POSTGRES_DB']))) cls.set_db_connection(cls.temporary_template_db) with cls.db_connection: + with cls.db_connection.cursor() as curs: + curs.execute("CREATE EXTENSION pldbgapi;") # Postgres debug extension cls.populate_database() @classmethod diff --git a/models.yml b/models.yml index eb66f263..1e2094af 100644 --- a/models.yml +++ b/models.yml @@ -2438,6 +2438,7 @@ motion: required: true restriction_mode: C constant: true + unique: meeting_id, sequential_number title: type: string required: true @@ -2533,10 +2534,9 @@ motion: to: motion/all_origin_ids restriction_mode: A identical_motion_ids: - type: int[] + type: dummy[] # type relation-list - # to: motion/identical_motion_ids - description: Changed from relation-list to int[], because can't still be genmerated + description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number.Changed from relation-list to number[], because can't still be generated restriction_mode: C state_id: type: relation From e2988da8321bbd91150af7d29b79088771f9190b Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Mon, 18 Mar 2024 15:01:31 +0100 Subject: [PATCH 025/142] Some setup fixes --- dev/Dockerfile-postgres | 2 +- dev/Makefile | 6 +++--- dev/docker-compose.yml | 8 ++++---- dev/scripts/apply_db_schema.sh | 3 ++- dev/scripts/entrypoint.sh | 2 +- dev/scripts/wait-for-database.sh | 5 ++--- dev/tests/base.py | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dev/Dockerfile-postgres b/dev/Dockerfile-postgres index 0791157f..15a54805 100644 --- a/dev/Dockerfile-postgres +++ b/dev/Dockerfile-postgres @@ -5,4 +5,4 @@ RUN apt-get update RUN apt-get install -y --no-install-recommends apt-utils RUN apt-get install -y --no-install-recommends postgresql-15-pldebugger -EXPOSE 5432 \ No newline at end of file +EXPOSE 5432 diff --git a/dev/Makefile b/dev/Makefile index 37c4c011..8dfdc1dc 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -36,10 +36,10 @@ generate-relational-schema: python src/generate_sql_schema.py drop-database: - dropdb -f -e --if-exists -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} ${POSTGRES_DB} + dropdb -f -e --if-exists -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} create-database: - createdb -e -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} ${POSTGRES_DB} + createdb -e -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} apply-db-schema: scripts/apply_db_schema.sh @@ -47,7 +47,7 @@ apply-db-schema: create-database-with-schema: drop-database create-database apply-db-schema run-psql: - psql -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} -d ${POSTGRES_DB} + psql -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} -d ${DATABASE_NAME} # Docker manage commands diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index 8ca76a3f..f7d0cd87 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -8,10 +8,10 @@ services: volumes: - ..:/app environment: - - POSTGRES_HOST=postgres - - POSTGRES_PORT=5432 - - POSTGRES_USER=openslides - - POSTGRES_DB=openslides + - DATABASE_HOST=postgres + - DATABASE_PORT=5432 + - DATABASE_USER=openslides + - DATABASE_NAME=openslides - PGPASSWORD=openslides depends_on: diff --git a/dev/scripts/apply_db_schema.sh b/dev/scripts/apply_db_schema.sh index 11f7e297..d7063960 100755 --- a/dev/scripts/apply_db_schema.sh +++ b/dev/scripts/apply_db_schema.sh @@ -1,3 +1,4 @@ #!/bin/bash -psql -1 -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f sql/schema_relational.sql +cd "$(dirname "$0")" +psql -1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/schema_relational.sql diff --git a/dev/scripts/entrypoint.sh b/dev/scripts/entrypoint.sh index d0c0b31e..a24dce57 100755 --- a/dev/scripts/entrypoint.sh +++ b/dev/scripts/entrypoint.sh @@ -3,4 +3,4 @@ scripts/wait-for-database.sh scripts/apply_db_schema.sh -exec "$@" \ No newline at end of file +exec "$@" diff --git a/dev/scripts/wait-for-database.sh b/dev/scripts/wait-for-database.sh index 18a7d51c..b6110ff3 100755 --- a/dev/scripts/wait-for-database.sh +++ b/dev/scripts/wait-for-database.sh @@ -1,7 +1,6 @@ #!/bin/bash -until pg_isready -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER"; do - echo "Waiting for Postgres server '$POSTGRES_HOST' to become available..." +until pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER"; do + echo "Waiting for Postgres server '$DATABASE_HOST' to become available..." sleep 3 done - diff --git a/dev/tests/base.py b/dev/tests/base.py index 82d12445..be0643dc 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -33,7 +33,7 @@ class BaseTestCase(TestCase): def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:callable = psycopg.rows.dict_row) -> None: env = os.environ try: - cls.db_connection = psycopg.connect(f"dbname='{db_name}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'", autocommit=autocommit, row_factory=row_factory) + cls.db_connection = psycopg.connect(f"dbname='{db_name}' user='{env['DATABASE_USER']}' host='{env['DATABASE_HOST']}' password='{env['PGPASSWORD']}'", autocommit=autocommit, row_factory=row_factory) except Exception as e: raise Exception(f"Cannot connect to postgres: {e.message}") @@ -51,7 +51,7 @@ def setup_class(cls): curs.execute( sql.SQL("CREATE DATABASE {db_to_create} TEMPLATE {template_db};").format( db_to_create=sql.Identifier(cls.temporary_template_db), - template_db=sql.Identifier(env['POSTGRES_DB']))) + template_db=sql.Identifier(env['DATABASE_NAME']))) cls.set_db_connection(cls.temporary_template_db) with cls.db_connection: cls.populate_database() From b0f6856706d1e39c9684a0747ea9aacd4c22e47c Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 19 Mar 2024 19:03:30 +0100 Subject: [PATCH 026/142] fix formal checks --- dev/src/db_utils.py | 21 +++++++++++++-------- dev/src/generate_sql_schema.py | 26 +++++++++++++++++--------- dev/src/helper_get_names.py | 11 +++++++---- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index 382b8d9d..d3dbbb16 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -1,11 +1,13 @@ -from typing import Any +from typing import Any, cast from psycopg import Cursor, sql class DbUtils: @classmethod - def insert_wrapper(cls, curs: Cursor, table_name: str, data: dict[str, Any]) -> int: + def insert_wrapper( + cls, curs: Cursor, table_name: str, data: dict[str, Any] + ) -> None | int: query = f"INSERT INTO {table_name} ({', '.join(data.keys())}) VALUES({{}}) RETURNING id;" query = ( sql.SQL(query) @@ -14,7 +16,10 @@ def insert_wrapper(cls, curs: Cursor, table_name: str, data: dict[str, Any]) -> ) .as_string(curs) ) - return curs.execute(query, tuple(data.values())).fetchone()["id"] + result = curs.execute(query, tuple(data.values())).fetchone() + if isinstance(result, dict): + return result.get("id", 0) + return None @classmethod def insert_many_wrapper( @@ -28,10 +33,10 @@ def insert_many_wrapper( if not data_list: return ids # use all keys in same sequence - keys = set() + keys_set: set = set() for data in data_list: - keys.update(data.keys()) - keys = sorted(keys) + keys_set.update(data.keys()) + keys: list = sorted(keys_set) temp_data = {k: None for k in keys} dates = [temp_data | data for data in data_list] @@ -51,7 +56,7 @@ def insert_many_wrapper( ids = [] if returning: while True: - ids.append(curs.fetchone()[returning]) + ids.append(cast(dict, curs.fetchone())[returning]) if not curs.nextset(): break return ids @@ -69,6 +74,6 @@ def select_id_wrapper( f"SELECT {', '.join(field_names) if field_names else '*'} FROM {table_name}{' where id = %s' if id_ else ''}" ) if id_: - return curs.execute(query, (id_,)).fetchone() + return result if (result := curs.execute(query, (id_,)).fetchone()) else {} else: return curs.execute(query).fetchall() diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 4b61ad49..48e55f8a 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -65,7 +65,7 @@ class GenerateCodeBlocks: ) # Key=Name, data: collected content of table @classmethod - def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str]: + def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -338,7 +338,7 @@ def get_relation_list_type( foreign_table_field_ref_id = cast(str, foreign_table_field.ref_column) if foreign_table_column or foreign_table_field_ref_id: if ( - type_ := foreign_table_field.field_def.get("type") + type_ := foreign_table_field.field_def.get("type", "") ) == "generic-relation": own_ref_column = own_table_field.ref_column foreign_table_column += ( @@ -391,8 +391,14 @@ def get_relation_list_type( foreign_table_ref_column, ) if own_table_field.field_def.get("required"): - text["create_trigger"] = cls.get_trigger_check_not_null_for_relation_lists( - own_table_field.table, own_table_field.column, foreign_table_field.table, foreign_table_field.column) + text["create_trigger"] = ( + cls.get_trigger_check_not_null_for_relation_lists( + own_table_field.table, + own_table_field.column, + foreign_table_field.table, + foreign_table_field.column, + ) + ) final_info = "SQL " + final_info text["final_info"] = final_info return text @@ -417,7 +423,9 @@ def get_sql_for_relation_n_1( return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter}) as {fname},\n" @classmethod - def get_trigger_check_not_null_for_relation_lists(cls, own_table:str, own_column:str, foreign_table:str, foreign_column) -> str: + def get_trigger_check_not_null_for_relation_lists( + cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str + ) -> str: foreign_table_t = HelperGetNames.get_table_name(foreign_table) return dedent( f""" @@ -435,7 +443,7 @@ def get_trigger_check_not_null_for_relation_lists(cls, own_table:str, own_column def get_generic_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> SchemaZoneTexts: - text: SchemaZoneTexts = defaultdict(str) + text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_fields: list[TableFieldType] = ( ModelsHelper.get_definitions_from_foreign_list( @@ -569,7 +577,7 @@ class Helper: -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name of field to check, usually a field in a view -- column_name of field to check - -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null + -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null. DECLARE table_name TEXT; column_name TEXT; @@ -800,7 +808,7 @@ def get_foreign_key_table_column( to: str | None, reference: str | None ) -> tuple[str, str]: if reference: - result = Helper.ref_compiled.search(reference) + result = InternalHelper.ref_compiled.search(reference) if result is None: return reference.strip(), "id" re_groups = result.groups() @@ -1234,7 +1242,7 @@ def main() -> None: if len(sys.argv) > 1: file = sys.argv[1] else: - file = SOURCE + file = str(SOURCE) MODELS, checksum = InternalHelper.read_models_yml(file) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 78e4aad6..f684ec89 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -162,10 +162,11 @@ def get_minlength_constraint_name( @staticmethod @max_length def get_not_null_rel_list_insert_trigger_name( - table_name: str, column_name: str, + table_name: str, + column_name: str, ) -> str: """gets the name of the insert trigger for not null on relation lists""" - name = f"tr_i_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + name = f"tr_i_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") HelperGetNames.trigger_unique_list.append(name) @@ -174,15 +175,17 @@ def get_not_null_rel_list_insert_trigger_name( @staticmethod @max_length def get_not_null_rel_list_upd_del_trigger_name( - table_name: str, column_name: str, + table_name: str, + column_name: str, ) -> str: """gets the name of the update/delete trigger for not null on relation lists""" - name = f"tr_ud_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + name = f"tr_ud_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") HelperGetNames.trigger_unique_list.append(name) return name + class InternalHelper: MODELS: dict[str, dict[str, Any]] = {} checksum: str = "" From 171f02d3eb9e47179b17d02ef42f4b0d94b62f7a Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 19 Mar 2024 20:16:22 +0100 Subject: [PATCH 027/142] better documented models.yml --- models.yml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/models.yml b/models.yml index 1e2094af..86bd5c93 100644 --- a/models.yml +++ b/models.yml @@ -8,18 +8,25 @@ # At the moment we support only X == 6. # - timestamp: Datetime as a unix timestamp. Why a number? This enables queries # in the DB. And we do not need more precision than 1 second. -# - []: This indicates and arbitrary array of the given type. At the moment +# - []: This indicates an arbitrary array of the given type. At the moment # we support only some types. You can add JSON Schema properties for items # using the extra property `items` # - color: string that must match ^#[0-9a-f]{6}$ # Relations: # - We have the following types: `relation`, `relation-list`, `generic-relation` -# and `generic-relation-list`. +# and `generic-relation-list`. All relation-types with a `list`-suffix are **always** realized as sql-statements, +# in case of n:m-relations with an intermediary table. # - Non-generic relations: The simple syntax for such a field -# `to: /`. This is a reference to a collection. The reverse -# relation field in this collection is . E. g. in a motion the field `meeting_id` -# `reference: , where the collection is the same as that from the `to`. +# `to: /`. This is a reference to a collection-field, the "other" side of a relation. +# In the relational DB this will be a field in a view filled by an automatically generated sql. +# During the development of the relational db one relation can have a `to` and a `reference` property. +# In this case the `to` value is only used for historical reason, onky the `reference` property is used +# for the schema generation. +# If necessary the sql can be written manually, see `sql`. +# `reference: Date: Wed, 20 Mar 2024 08:55:57 +0100 Subject: [PATCH 028/142] Disable model.yml validation in CI --- .github/workflows/continuous_integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 6c91ed07..ad8a6e62 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -48,6 +48,6 @@ jobs: if: always() run: make pyupgrade - - name: Validate models.yml - if: always() - run: make validate-models + # - name: Validate models.yml + # if: always() + # run: make validate-models From 11fc9048f15f3a2beb9c4e9db150fb91035766dd Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 22 Mar 2024 19:24:26 +0100 Subject: [PATCH 029/142] add *.code-workspace to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 27a914ce..5e504384 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ migrations/*.sql # secrets secrets/ + +# code-workspaces +*.code-workspace From 6a61d07929694c77c3e13abc3a605cdd3b20b601 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 25 Mar 2024 19:36:14 +0100 Subject: [PATCH 030/142] intermediate checks --- dev/src/generate_sql_schema.py | 22 ++++++++++++++++------ dev/src/helper_get_names.py | 11 +++++++---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 98c4e197..79d020ff 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -178,7 +178,7 @@ def get_method( else: type_ = fdata.get("type", "") if type_ in FIELD_TYPES: - if (method := FIELD_TYPES[type_].get("method")): + if method := FIELD_TYPES[type_].get("method"): return (method.__get__(cls), type_) # returns the callable classmethod else: text = "no method defined" @@ -394,8 +394,14 @@ def get_relation_list_type( foreign_table_ref_column, ) if own_table_field.field_def.get("required"): - text["create_trigger"] = cls.get_trigger_check_not_null_for_relation_lists( - own_table_field.table, own_table_field.column, foreign_table_field.table, foreign_table_field.column) + text["create_trigger"] = ( + cls.get_trigger_check_not_null_for_relation_lists( + own_table_field.table, + own_table_field.column, + foreign_table_field.table, + foreign_table_field.column, + ) + ) final_info = "SQL " + final_info text["final_info"] = final_info return text @@ -420,7 +426,9 @@ def get_sql_for_relation_n_1( return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter}) as {fname},\n" @classmethod - def get_trigger_check_not_null_for_relation_lists(cls, own_table:str, own_column:str, foreign_table:str, foreign_column) -> str: + def get_trigger_check_not_null_for_relation_lists( + cls, own_table: str, own_column: str, foreign_table: str, foreign_column + ) -> str: foreign_table_t = HelperGetNames.get_table_name(foreign_table) return dedent( f""" @@ -572,7 +580,7 @@ class Helper: -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name of field to check, usually a field in a view -- column_name of field to check - -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null + -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null. DECLARE table_name TEXT; column_name TEXT; @@ -1006,7 +1014,9 @@ def check_relation_definitions( for foreign_field in foreign_fields: foreign_c, tmp_error = Helper.get_cardinality(foreign_field.field_def) if own_c == "1tR" and foreign_c == "1r": - raise Exception(f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1tR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut.") + raise Exception( + f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1tR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut." + ) foreigns_c.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 78e4aad6..f684ec89 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -162,10 +162,11 @@ def get_minlength_constraint_name( @staticmethod @max_length def get_not_null_rel_list_insert_trigger_name( - table_name: str, column_name: str, + table_name: str, + column_name: str, ) -> str: """gets the name of the insert trigger for not null on relation lists""" - name = f"tr_i_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + name = f"tr_i_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") HelperGetNames.trigger_unique_list.append(name) @@ -174,15 +175,17 @@ def get_not_null_rel_list_insert_trigger_name( @staticmethod @max_length def get_not_null_rel_list_upd_del_trigger_name( - table_name: str, column_name: str, + table_name: str, + column_name: str, ) -> str: """gets the name of the update/delete trigger for not null on relation lists""" - name = f"tr_ud_{table_name}_{column_name}"[:HelperGetNames.MAX_LEN] + name = f"tr_ud_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") HelperGetNames.trigger_unique_list.append(name) return name + class InternalHelper: MODELS: dict[str, dict[str, Any]] = {} checksum: str = "" From 1db901022f7252dd7bc2360a200351000a8b007c Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Mon, 25 Mar 2024 19:43:12 +0100 Subject: [PATCH 031/142] draft: started get relation letters in check_relation_definitions --- dev/doc/generate_sql_schema.md | 6 +++-- dev/src/generate_sql_schema.py | 41 ++++++++++++++++++++++++++++------ dev/tests/base.py | 1 + 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/dev/doc/generate_sql_schema.md b/dev/doc/generate_sql_schema.md index efb68334..58183ac0 100644 --- a/dev/doc/generate_sql_schema.md +++ b/dev/doc/generate_sql_schema.md @@ -5,7 +5,7 @@ The length will be checked by the following methods on generating the sql-schema ## The type TableFieldType -This type is used sometimes for the parameters of the methods. It can be build by it's constructor or as convinience with the static method **get_definitions_from_foreign**, which takes as parametes +This type is used sometimes for the parameters of the methods. It can be build by it's constructor or as convinience with the static method **get_definitions_from_foreign**, which takes as parameters the str-values from the **to**- and the **reference**-Attribut from models field description. It contains the **table_name**, **fname** for the field name, the dictionary **tfield** from the models.yml with all attributes and a **ref_column**, usually filled with **id** for the foreign-key definitions. @@ -38,4 +38,6 @@ The name of the intermediate table for **generic-relation-list** versus **relati * gm_ Constant part to mark a genericc-list:m intermediate table * table name of the generic-relation-list field * _ Constant divider -* field name of the generic-relation-list field \ No newline at end of file +* field name of the generic-relation-list field + +## Attributes and rules \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 79d020ff..a8c96e6c 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -43,6 +43,11 @@ class SQL_Delete_Update_Options(str, Enum): NO_ACTION = "NO ACTION" +class FIELD_SQL_ERROR_ENUM(Enum): + FIELD = 1 + SQL = 2 + ERROR = 3 + class SubstDict(TypedDict, total=False): """dict for substitutions of field templates""" @@ -952,10 +957,11 @@ def get_initials( return subst, text @staticmethod - def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: + def get_cardinality(field_all: TableFieldType) -> tuple[str, bool]: """ Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) """ + field = field_all.field_def if field: required = bool(field.get("required")) sql = "sql" in field @@ -1004,15 +1010,30 @@ def get_cardinality(field: dict[str, Any] | None) -> tuple[str, bool]: @staticmethod def check_relation_definitions( own_field: TableFieldType, foreign_fields: list[TableFieldType] - ) -> tuple[str, bool]: + ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str, str|None]: + """ + Decides for the own-field, + - whether it is a field, a sql-expression or is there an error + - relation-list and generic-relation-list are always sql-expressions. + True significates, that it is the pimary that creates the intermediate table + + Also checks relational behaviour and produces the informative relation line and in + case of an error an error text + + Returns: + - field, sql, error => enum + - primary field (only relevant for list fields) + - complete relational text with FIELD, SQL or *** in front + - error line if error + """ error = False text = "" - own_c, tmp_error = Helper.get_cardinality(own_field.field_def) + own_c, tmp_error = Helper.get_cardinality(own_field) error = error or tmp_error foreigns_c = [] foreign_collectionfields = [] for foreign_field in foreign_fields: - foreign_c, tmp_error = Helper.get_cardinality(foreign_field.field_def) + foreign_c, tmp_error = Helper.get_cardinality(foreign_field) if own_c == "1tR" and foreign_c == "1r": raise Exception( f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1tR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut." @@ -1021,6 +1042,9 @@ def check_relation_definitions( error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) + # todo: generate_field_view_or_nothing hier einfügen, Rückgabe feld oder select + # und bei listen auch primär, sekundär + # Fehlertext hier auch erzeugen (2 zeiig in relliste) if error: text = "*** " text += f"{own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" @@ -1030,9 +1054,11 @@ def check_relation_definitions( def generate_field_view_or_nothing( own: TableFieldType, foreign: TableFieldType ) -> bool: - """Decides, whether a relation field will be physical, view field or nothing - Returns True = if necessary build table, view or intermediate Tables etc. - False = do only extra + """ + Decides for the own-field + - relation-list and generic-relation-list are always sql-expressions. + True significates, that it is the pimary that creates the intermediate table + - for relation and generic-relation True marks a field, False a sql-expression """ decision_list = { ("relation", "relation"): "decide_primary_side", @@ -1069,6 +1095,7 @@ def generate_field_view_or_nothing( f"Type combination not implemented: {own_type}:{foreign_type} on field {own.collectionfield}" ) elif result == "decide_primary_side": + #TODO: mehr auf fehler checken if own.field_def.get("required", False) == foreign.field_def.get( "required", False ): diff --git a/dev/tests/base.py b/dev/tests/base.py index f696a5a6..de832de7 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -34,6 +34,7 @@ def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:cal env = os.environ try: cls.db_connection = psycopg.connect(f"dbname='{db_name}' user='{env['POSTGRES_USER']}' host='{env['POSTGRES_HOST']}' password='{env['PGPASSWORD']}'", autocommit=autocommit, row_factory=row_factory) + cls.db_connection.isolation_level = psycopg.IsolationLevel.SERIALIZABLE except Exception as e: raise Exception(f"Cannot connect to postgres: {e.message}") From a5c7bac54897cbfcc457662e15f2a735bfeabb99 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 28 Mar 2024 09:37:11 +0100 Subject: [PATCH 032/142] fix some small errors in models.yml --- models.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/models.yml b/models.yml index 86bd5c93..770a7fe1 100644 --- a/models.yml +++ b/models.yml @@ -15,17 +15,17 @@ # Relations: # - We have the following types: `relation`, `relation-list`, `generic-relation` # and `generic-relation-list`. All relation-types with a `list`-suffix are **always** realized as sql-statements, -# in case of n:m-relations with an intermediary table. +# in case of n:m-relations with an intermediary table. # - Non-generic relations: The simple syntax for such a field # `to: /`. This is a reference to a collection-field, the "other" side of a relation. # In the relational DB this will be a field in a view filled by an automatically generated sql. # During the development of the relational db one relation can have a `to` and a `reference` property. # In this case the `to` value is only used for historical reason, onky the `reference` property is used -# for the schema generation. +# for the schema generation. # If necessary the sql can be written manually, see `sql`. # `reference: Date: Thu, 28 Mar 2024 09:42:11 +0100 Subject: [PATCH 033/142] add .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5e504384..34a682ff 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ secrets/ # code-workspaces *.code-workspace + +.gitignore From 156b6ea53033e1f668dfd40af2e258ff35b7721b Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 3 Apr 2024 15:44:00 +0200 Subject: [PATCH 034/142] models.yml fixed with all needed reference attributes --- dev/sql/schema_relational.sql | 560 ++++++++++++++-------------- dev/src/generate_sql_schema.py | 380 +++++++++---------- dev/src/helper_get_names.py | 18 +- dev/tests/test_generic_relations.py | 9 + models.yml | 150 +++++++- 5 files changed, 613 insertions(+), 504 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index c444c3ab..a3c928b8 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -7,7 +7,7 @@ create or replace function check_not_null_for_relation_lists() returns trigger a -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name of field to check, usually a field in a view -- column_name of field to check --- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null +-- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null. DECLARE table_name TEXT; column_name TEXT; @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'be37c20e734ef040be86fbb8f0d4b665' +-- MODELS_YML_CHECKSUM = 'a0ab7cba150b1eeae6c16bb3e658496a' -- Type definitions DO $$ BEGIN @@ -672,6 +672,7 @@ CREATE TABLE IF NOT EXISTS meetingT ( list_of_speakers_show_amount_of_speakers_on_slide boolean DEFAULT True, list_of_speakers_present_users_only boolean DEFAULT False, list_of_speakers_show_first_contribution boolean DEFAULT False, + list_of_speakers_hide_contribution_count boolean DEFAULT False, list_of_speakers_allow_multiple_speakers boolean DEFAULT False, list_of_speakers_enable_point_of_order_speakers boolean DEFAULT True, list_of_speakers_can_create_point_of_order_for_others boolean DEFAULT False, @@ -984,6 +985,7 @@ CREATE TABLE IF NOT EXISTS motionT ( workflow_timestamp timestamptz, start_line_number integer CONSTRAINT minimum_start_line_number CHECK (start_line_number >= 1) DEFAULT 1, forwarded timestamptz, + additional_submitter varchar(256), lead_motion_id integer, sort_parent_id integer, origin_id integer, @@ -2270,7 +2272,6 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa Generated: What will be generated for left field FIELD: a usual Database field SQL: a sql-expression in a view - NOTHING: still nothing ***: Error Field Attributes:Field Attributes opposite side 1: cardinality 1 @@ -2279,109 +2280,108 @@ Field Attributes:Field Attributes opposite side nG: cardinality n with generic-relation-list field t: "to" defined r: "reference" defined - s: sql directive given, but must be generated - s+: sql directive inclusive sql-statement + s: sql directive inclusive sql-statement R: Required Model.Field -> Model.Field model.field names */ /* -SQL ns+: => organization/committee_ids:-> - -SQL nt:1t => organization/active_meeting_ids:-> meeting/is_active_in_organization_id -SQL nt:1t => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id -SQL nt:1t => organization/template_meeting_ids:-> meeting/template_for_organization_id +SQL nrs: => organization/committee_ids:-> committee/ +SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id +SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id +SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id SQL nr: => organization/organization_tag_ids:-> organization_tag/ FIELD 1rR: => organization/theme_id:-> theme/ SQL nr: => organization/theme_ids:-> theme/ -SQL nt:1GtR => organization/mediafile_ids:-> mediafile/owner_id +SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id SQL nr: => organization/user_ids:-> user/ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids -SQL nt:1t => user/forwarding_committee_ids:-> committee/forwarding_user_id -SQL nt:1tR => user/meeting_user_ids:-> meeting_user/user_id +SQL nt:1r => user/forwarding_committee_ids:-> committee/forwarding_user_id +SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids -SQL nt:1Gt => user/option_ids:-> option/content_object_id -SQL nt:1t => user/vote_ids:-> vote/user_id -SQL nt:1t => user/delegated_vote_ids:-> vote/delegated_user_id -SQL nt:1t => user/poll_candidate_ids:-> poll_candidate/user_id - -FIELD 1tR:nt => meeting_user/user_id:-> user/meeting_user_ids -FIELD 1tR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids -SQL nt:1tR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id -SQL nt:1t => meeting_user/speaker_ids:-> speaker/meeting_user_id +SQL nt:1Gr => user/option_ids:-> option/content_object_id +SQL nt:1r => user/vote_ids:-> vote/user_id +SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id +SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id + +FIELD 1rR: => meeting_user/user_id:-> user/ +FIELD 1rR: => meeting_user/meeting_id:-> meeting/ +SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id +SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids -SQL nt:1tR => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id -SQL nt:1tR => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id -SQL nt:1tR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id -SQL nt:1t => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id -FIELD 1t:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids -SQL nt:1t => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id -SQL nt:1tR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id +SQL nt:1rR => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id +SQL nt:1rR => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id +SQL nt:1rR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id +SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id +FIELD 1r: => meeting_user/vote_delegated_to_id:-> meeting_user/ +SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id +SQL nt:1rR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids -FIELD nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids +SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id -SQL nt:1tR => committee/meeting_ids:-> meeting/committee_id +SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r: => committee/default_meeting_id:-> meeting/ SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids -FIELD 1t:nt => committee/forwarding_user_id:-> user/forwarding_committee_ids +FIELD 1r: => committee/forwarding_user_id:-> user/ SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids -FIELD 1t:nt => meeting/is_active_in_organization_id:-> organization/active_meeting_ids -FIELD 1t:nt => meeting/is_archived_in_organization_id:-> organization/archived_meeting_ids -FIELD 1t:nt => meeting/template_for_organization_id:-> organization/template_meeting_ids -FIELD 1tR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id -FIELD 1tR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id -FIELD 1tR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id -SQL nt:1t => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id -SQL nt:1tR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id -SQL nt:1tR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id -SQL nt:1tR => meeting/meeting_user_ids:-> meeting_user/meeting_id -SQL nt:1t => meeting/assignment_poll_default_group_ids:-> group/used_as_assignment_poll_default_id -SQL nt:1t => meeting/poll_default_group_ids:-> group/used_as_poll_default_id -SQL nt:1t => meeting/topic_poll_default_group_ids:-> group/used_as_topic_poll_default_id -SQL nt:1tR => meeting/projector_ids:-> projector/meeting_id -SQL nt:1tR => meeting/all_projection_ids:-> projection/meeting_id -SQL nt:1tR => meeting/projector_message_ids:-> projector_message/meeting_id -SQL nt:1tR => meeting/projector_countdown_ids:-> projector_countdown/meeting_id -SQL nt:1tR => meeting/tag_ids:-> tag/meeting_id -SQL nt:1tR => meeting/agenda_item_ids:-> agenda_item/meeting_id -SQL nt:1tR => meeting/list_of_speakers_ids:-> list_of_speakers/meeting_id -SQL nt:1tR => meeting/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/meeting_id -SQL nt:1tR => meeting/point_of_order_category_ids:-> point_of_order_category/meeting_id -SQL nt:1tR => meeting/speaker_ids:-> speaker/meeting_id -SQL nt:1tR => meeting/topic_ids:-> topic/meeting_id -SQL nt:1tR => meeting/group_ids:-> group/meeting_id -SQL nt:1GtR => meeting/mediafile_ids:-> mediafile/owner_id -SQL nt:1tR => meeting/motion_ids:-> motion/meeting_id -SQL nt:1t => meeting/forwarded_motion_ids:-> motion/origin_meeting_id -SQL nt:1tR => meeting/motion_comment_section_ids:-> motion_comment_section/meeting_id -SQL nt:1tR => meeting/motion_category_ids:-> motion_category/meeting_id -SQL nt:1tR => meeting/motion_block_ids:-> motion_block/meeting_id -SQL nt:1tR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id -SQL nt:1tR => meeting/motion_statute_paragraph_ids:-> motion_statute_paragraph/meeting_id -SQL nt:1tR => meeting/motion_comment_ids:-> motion_comment/meeting_id -SQL nt:1tR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id -SQL nt:1tR => meeting/motion_editor_ids:-> motion_editor/meeting_id -SQL nt:1tR => meeting/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_id -SQL nt:1tR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id -SQL nt:1tR => meeting/motion_state_ids:-> motion_state/meeting_id -SQL nt:1tR => meeting/poll_ids:-> poll/meeting_id -SQL nt:1tR => meeting/option_ids:-> option/meeting_id -SQL nt:1tR => meeting/vote_ids:-> vote/meeting_id -SQL nt:1tR => meeting/assignment_ids:-> assignment/meeting_id -SQL nt:1tR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id -SQL nt:1tR => meeting/personal_note_ids:-> personal_note/meeting_id -SQL nt:1tR => meeting/chat_group_ids:-> chat_group/meeting_id -SQL nt:1tR => meeting/chat_message_ids:-> chat_message/meeting_id -SQL nt:1tR => meeting/structure_level_ids:-> structure_level/meeting_id +FIELD 1r: => meeting/is_active_in_organization_id:-> organization/ +FIELD 1r: => meeting/is_archived_in_organization_id:-> organization/ +FIELD 1r: => meeting/template_for_organization_id:-> organization/ +FIELD 1rR: => meeting/motions_default_workflow_id:-> motion_workflow/ +FIELD 1rR: => meeting/motions_default_amendment_workflow_id:-> motion_workflow/ +FIELD 1rR: => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/ +SQL nt:1r => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id +SQL nt:1rR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id +SQL nt:1rR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id +SQL nt:1rR => meeting/meeting_user_ids:-> meeting_user/meeting_id +SQL nt:1r => meeting/assignment_poll_default_group_ids:-> group/used_as_assignment_poll_default_id +SQL nt:1r => meeting/poll_default_group_ids:-> group/used_as_poll_default_id +SQL nt:1r => meeting/topic_poll_default_group_ids:-> group/used_as_topic_poll_default_id +SQL nt:1rR => meeting/projector_ids:-> projector/meeting_id +SQL nt:1rR => meeting/all_projection_ids:-> projection/meeting_id +SQL nt:1rR => meeting/projector_message_ids:-> projector_message/meeting_id +SQL nt:1rR => meeting/projector_countdown_ids:-> projector_countdown/meeting_id +SQL nt:1rR => meeting/tag_ids:-> tag/meeting_id +SQL nt:1rR => meeting/agenda_item_ids:-> agenda_item/meeting_id +SQL nt:1rR => meeting/list_of_speakers_ids:-> list_of_speakers/meeting_id +SQL nt:1rR => meeting/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/meeting_id +SQL nt:1rR => meeting/point_of_order_category_ids:-> point_of_order_category/meeting_id +SQL nt:1rR => meeting/speaker_ids:-> speaker/meeting_id +SQL nt:1rR => meeting/topic_ids:-> topic/meeting_id +SQL nt:1rR => meeting/group_ids:-> group/meeting_id +SQL nt:1GrR => meeting/mediafile_ids:-> mediafile/owner_id +SQL nt:1rR => meeting/motion_ids:-> motion/meeting_id +SQL nt:1r => meeting/forwarded_motion_ids:-> motion/origin_meeting_id +SQL nt:1rR => meeting/motion_comment_section_ids:-> motion_comment_section/meeting_id +SQL nt:1rR => meeting/motion_category_ids:-> motion_category/meeting_id +SQL nt:1rR => meeting/motion_block_ids:-> motion_block/meeting_id +SQL nt:1rR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id +SQL nt:1rR => meeting/motion_statute_paragraph_ids:-> motion_statute_paragraph/meeting_id +SQL nt:1rR => meeting/motion_comment_ids:-> motion_comment/meeting_id +SQL nt:1rR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id +SQL nt:1rR => meeting/motion_editor_ids:-> motion_editor/meeting_id +SQL nt:1rR => meeting/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_id +SQL nt:1rR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id +SQL nt:1rR => meeting/motion_state_ids:-> motion_state/meeting_id +SQL nt:1rR => meeting/poll_ids:-> poll/meeting_id +SQL nt:1rR => meeting/option_ids:-> option/meeting_id +SQL nt:1rR => meeting/vote_ids:-> vote/meeting_id +SQL nt:1rR => meeting/assignment_ids:-> assignment/meeting_id +SQL nt:1rR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id +SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id +SQL nt:1rR => meeting/chat_group_ids:-> chat_group/meeting_id +SQL nt:1rR => meeting/chat_message_ids:-> chat_message/meeting_id +SQL nt:1rR => meeting/structure_level_ids:-> structure_level/meeting_id FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ @@ -2398,37 +2398,37 @@ FIELD 1r: => meeting/font_monospace_id:-> mediafile/ FIELD 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ FIELD 1r: => meeting/font_projector_h1_id:-> mediafile/ FIELD 1r: => meeting/font_projector_h2_id:-> mediafile/ -FIELD 1tR:nt => meeting/committee_id:-> committee/meeting_ids +FIELD 1rR: => meeting/committee_id:-> committee/ SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids -FIELD 1tR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id +FIELD 1rR: => meeting/reference_projector_id:-> projector/ FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ -SQL nt:1GtR => meeting/projection_ids:-> projection/content_object_id -SQL ntR:1t => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id -SQL ntR:1t => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id -SQL ntR:1t => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id -SQL ntR:1t => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id -SQL ntR:1t => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id -SQL ntR:1t => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id -SQL ntR:1t => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id -SQL ntR:1t => meeting/default_projector_assignment_ids:-> projector/used_as_default_projector_for_assignment_in_meeting_id -SQL ntR:1t => meeting/default_projector_mediafile_ids:-> projector/used_as_default_projector_for_mediafile_in_meeting_id -SQL ntR:1t => meeting/default_projector_message_ids:-> projector/used_as_default_projector_for_message_in_meeting_id -SQL ntR:1t => meeting/default_projector_countdown_ids:-> projector/used_as_default_projector_for_countdown_in_meeting_id -SQL ntR:1t => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id -SQL ntR:1t => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id -SQL ntR:1t => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id -FIELD 1tR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id +SQL nt:1GrR => meeting/projection_ids:-> projection/content_object_id +SQL ntR:1r => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id +SQL ntR:1r => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id +SQL ntR:1r => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id +SQL ntR:1r => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id +SQL ntR:1r => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id +SQL ntR:1r => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id +SQL ntR:1r => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id +SQL ntR:1r => meeting/default_projector_assignment_ids:-> projector/used_as_default_projector_for_assignment_in_meeting_id +SQL ntR:1r => meeting/default_projector_mediafile_ids:-> projector/used_as_default_projector_for_mediafile_in_meeting_id +SQL ntR:1r => meeting/default_projector_message_ids:-> projector/used_as_default_projector_for_message_in_meeting_id +SQL ntR:1r => meeting/default_projector_countdown_ids:-> projector/used_as_default_projector_for_countdown_in_meeting_id +SQL ntR:1r => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id +SQL ntR:1r => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id +SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id +FIELD 1rR: => meeting/default_group_id:-> group/ FIELD 1r: => meeting/admin_group_id:-> group/ SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids -SQL nt:1tR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id -FIELD 1tR:nt => structure_level/meeting_id:-> meeting/structure_level_ids +SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id +FIELD 1rR: => structure_level/meeting_id:-> meeting/ SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids -SQL 1t:1tR => group/default_group_for_meeting_id:-> meeting/default_group_id +SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids @@ -2437,190 +2437,190 @@ SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_gro SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids SQL nt:nt => group/write_chat_group_ids:-> chat_group/write_group_ids SQL nt:nt => group/poll_ids:-> poll/entitled_group_ids -FIELD 1t:nt => group/used_as_motion_poll_default_id:-> meeting/motion_poll_default_group_ids -FIELD 1t:nt => group/used_as_assignment_poll_default_id:-> meeting/assignment_poll_default_group_ids -FIELD 1t:nt => group/used_as_topic_poll_default_id:-> meeting/topic_poll_default_group_ids -FIELD 1t:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids -FIELD 1tR:nt => group/meeting_id:-> meeting/group_ids - -FIELD 1tR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids -FIELD 1Gt:nt => personal_note/content_object_id:-> motion/personal_note_ids -FIELD 1tR:nt => personal_note/meeting_id:-> meeting/personal_note_ids - -FIELD nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids -FIELD 1tR:nt => tag/meeting_id:-> meeting/tag_ids - -FIELD 1GtR:1t,1t,1t,1tR => agenda_item/content_object_id:-> motion/agenda_item_id,motion_block/agenda_item_id,assignment/agenda_item_id,topic/agenda_item_id -FIELD 1t:nt => agenda_item/parent_id:-> agenda_item/child_ids -SQL nt:1t => agenda_item/child_ids:-> agenda_item/parent_id +FIELD 1r: => group/used_as_motion_poll_default_id:-> meeting/ +FIELD 1r: => group/used_as_assignment_poll_default_id:-> meeting/ +FIELD 1r: => group/used_as_topic_poll_default_id:-> meeting/ +FIELD 1r: => group/used_as_poll_default_id:-> meeting/ +FIELD 1rR: => group/meeting_id:-> meeting/ + +FIELD 1rR: => personal_note/meeting_user_id:-> meeting_user/ +FIELD 1Gr: => personal_note/content_object_id:-> motion/ +FIELD 1rR: => personal_note/meeting_id:-> meeting/ + +SQL nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids +FIELD 1rR: => tag/meeting_id:-> meeting/ + +FIELD 1GrR:,,, => agenda_item/content_object_id:-> motion/,motion_block/,assignment/,topic/ +FIELD 1r: => agenda_item/parent_id:-> agenda_item/ +SQL nt:1r => agenda_item/child_ids:-> agenda_item/parent_id SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids -SQL nt:1GtR => agenda_item/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids +SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id +FIELD 1rR: => agenda_item/meeting_id:-> meeting/ -FIELD 1GtR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,mediafile/list_of_speakers_id -SQL nt:1tR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id -SQL nt:1tR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id -SQL nt:1GtR => list_of_speakers/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids +FIELD 1GrR:,,,, => list_of_speakers/content_object_id:-> motion/,motion_block/,assignment/,topic/,mediafile/ +SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id +SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id +SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id +FIELD 1rR: => list_of_speakers/meeting_id:-> meeting/ -FIELD 1tR:nt => structure_level_list_of_speakers/structure_level_id:-> structure_level/structure_level_list_of_speakers_ids -FIELD 1tR:nt => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/structure_level_list_of_speakers_ids -SQL nt:1t => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id -FIELD 1tR:nt => structure_level_list_of_speakers/meeting_id:-> meeting/structure_level_list_of_speakers_ids +FIELD 1rR: => structure_level_list_of_speakers/structure_level_id:-> structure_level/ +FIELD 1rR: => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/ +SQL nt:1r => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id +FIELD 1rR: => structure_level_list_of_speakers/meeting_id:-> meeting/ -FIELD 1tR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids -SQL nt:1t => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id +FIELD 1rR: => point_of_order_category/meeting_id:-> meeting/ +SQL nt:1r => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id -FIELD 1tR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids -FIELD 1t:nt => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/speaker_ids -FIELD 1t:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids -FIELD 1t:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids -FIELD 1tR:nt => speaker/meeting_id:-> meeting/speaker_ids +FIELD 1rR: => speaker/list_of_speakers_id:-> list_of_speakers/ +FIELD 1r: => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/ +FIELD 1r: => speaker/meeting_user_id:-> meeting_user/ +FIELD 1r: => speaker/point_of_order_category_id:-> point_of_order_category/ +FIELD 1rR: => speaker/meeting_id:-> meeting/ SQL nt:nGt => topic/attachment_ids:-> mediafile/attachment_ids -SQL 1tR:1GtR => topic/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GtR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GtR => topic/poll_ids:-> poll/content_object_id -SQL nt:1GtR => topic/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => topic/meeting_id:-> meeting/topic_ids - -FIELD 1t:nt => motion/lead_motion_id:-> motion/amendment_ids -SQL nt:1t => motion/amendment_ids:-> motion/lead_motion_id -FIELD 1t:nt => motion/sort_parent_id:-> motion/sort_child_ids -SQL nt:1t => motion/sort_child_ids:-> motion/sort_parent_id -FIELD 1t:nt => motion/origin_id:-> motion/derived_motion_ids -FIELD 1t:nt => motion/origin_meeting_id:-> meeting/forwarded_motion_ids -SQL nt:1t => motion/derived_motion_ids:-> motion/origin_id +SQL 1tR:1GrR => topic/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => topic/poll_ids:-> poll/content_object_id +SQL nt:1GrR => topic/projection_ids:-> projection/content_object_id +FIELD 1rR: => topic/meeting_id:-> meeting/ + +FIELD 1r: => motion/lead_motion_id:-> motion/ +SQL nt:1r => motion/amendment_ids:-> motion/lead_motion_id +FIELD 1r: => motion/sort_parent_id:-> motion/ +SQL nt:1r => motion/sort_child_ids:-> motion/sort_parent_id +FIELD 1r: => motion/origin_id:-> motion/ +FIELD 1r: => motion/origin_meeting_id:-> meeting/ +SQL nt:1r => motion/derived_motion_ids:-> motion/origin_id SQL nt:nt => motion/all_origin_ids:-> motion/all_derived_motion_ids SQL nt:nt => motion/all_derived_motion_ids:-> motion/all_origin_ids -FIELD 1tR:nt => motion/state_id:-> motion_state/motion_ids -FIELD 1t:nt => motion/recommendation_id:-> motion_state/motion_recommendation_ids -FIELD nGt:nt => motion/state_extension_reference_ids:-> motion/referenced_in_motion_state_extension_ids +FIELD 1rR: => motion/state_id:-> motion_state/ +FIELD 1r: => motion/recommendation_id:-> motion_state/ +SQL nGt:nt => motion/state_extension_reference_ids:-> motion/referenced_in_motion_state_extension_ids SQL nt:nGt => motion/referenced_in_motion_state_extension_ids:-> motion/state_extension_reference_ids -FIELD nGt:nt => motion/recommendation_extension_reference_ids:-> motion/referenced_in_motion_recommendation_extension_ids +SQL nGt:nt => motion/recommendation_extension_reference_ids:-> motion/referenced_in_motion_recommendation_extension_ids SQL nt:nGt => motion/referenced_in_motion_recommendation_extension_ids:-> motion/recommendation_extension_reference_ids -FIELD 1t:nt => motion/category_id:-> motion_category/motion_ids -FIELD 1t:nt => motion/block_id:-> motion_block/motion_ids -SQL nt:1tR => motion/submitter_ids:-> motion_submitter/motion_id +FIELD 1r: => motion/category_id:-> motion_category/ +FIELD 1r: => motion/block_id:-> motion_block/ +SQL nt:1rR => motion/submitter_ids:-> motion_submitter/motion_id SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ids -SQL nt:1tR => motion/editor_ids:-> motion_editor/motion_id -SQL nt:1tR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id -SQL nt:1GtR => motion/poll_ids:-> poll/content_object_id -SQL nt:1Gt => motion/option_ids:-> option/content_object_id -SQL nt:1tR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id -FIELD 1t:nt => motion/statute_paragraph_id:-> motion_statute_paragraph/motion_ids -SQL nt:1tR => motion/comment_ids:-> motion_comment/motion_id -SQL 1t:1GtR => motion/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GtR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1rR => motion/editor_ids:-> motion_editor/motion_id +SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id +SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id +SQL nt:1Gr => motion/option_ids:-> option/content_object_id +SQL nt:1rR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id +FIELD 1r: => motion/statute_paragraph_id:-> motion_statute_paragraph/ +SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id +SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:nGt => motion/tag_ids:-> tag/tagged_ids SQL nt:nGt => motion/attachment_ids:-> mediafile/attachment_ids -SQL nt:1GtR => motion/projection_ids:-> projection/content_object_id -SQL nt:1Gt => motion/personal_note_ids:-> personal_note/content_object_id -FIELD 1tR:nt => motion/meeting_id:-> meeting/motion_ids +SQL nt:1GrR => motion/projection_ids:-> projection/content_object_id +SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id +FIELD 1rR: => motion/meeting_id:-> meeting/ -FIELD 1tR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids -FIELD 1tR:nt => motion_submitter/motion_id:-> motion/submitter_ids -FIELD 1tR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids +FIELD 1rR: => motion_submitter/meeting_user_id:-> meeting_user/ +FIELD 1rR: => motion_submitter/motion_id:-> motion/ +FIELD 1rR: => motion_submitter/meeting_id:-> meeting/ -FIELD 1tR:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids -FIELD 1tR:nt => motion_editor/motion_id:-> motion/editor_ids -FIELD 1tR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids +FIELD 1rR: => motion_editor/meeting_user_id:-> meeting_user/ +FIELD 1rR: => motion_editor/motion_id:-> motion/ +FIELD 1rR: => motion_editor/meeting_id:-> meeting/ -FIELD 1tR:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids -FIELD 1tR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids -FIELD 1tR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids +FIELD 1rR: => motion_working_group_speaker/meeting_user_id:-> meeting_user/ +FIELD 1rR: => motion_working_group_speaker/motion_id:-> motion/ +FIELD 1rR: => motion_working_group_speaker/meeting_id:-> meeting/ -FIELD 1tR:nt => motion_comment/motion_id:-> motion/comment_ids -FIELD 1tR:nt => motion_comment/section_id:-> motion_comment_section/comment_ids -FIELD 1tR:nt => motion_comment/meeting_id:-> meeting/motion_comment_ids +FIELD 1rR: => motion_comment/motion_id:-> motion/ +FIELD 1rR: => motion_comment/section_id:-> motion_comment_section/ +FIELD 1rR: => motion_comment/meeting_id:-> meeting/ -SQL nt:1tR => motion_comment_section/comment_ids:-> motion_comment/section_id +SQL nt:1rR => motion_comment_section/comment_ids:-> motion_comment/section_id SQL nt:nt => motion_comment_section/read_group_ids:-> group/read_comment_section_ids SQL nt:nt => motion_comment_section/write_group_ids:-> group/write_comment_section_ids -FIELD 1tR:nt => motion_comment_section/meeting_id:-> meeting/motion_comment_section_ids +FIELD 1rR: => motion_comment_section/meeting_id:-> meeting/ -FIELD 1t:nt => motion_category/parent_id:-> motion_category/child_ids -SQL nt:1t => motion_category/child_ids:-> motion_category/parent_id -SQL nt:1t => motion_category/motion_ids:-> motion/category_id -FIELD 1tR:nt => motion_category/meeting_id:-> meeting/motion_category_ids +FIELD 1r: => motion_category/parent_id:-> motion_category/ +SQL nt:1r => motion_category/child_ids:-> motion_category/parent_id +SQL nt:1r => motion_category/motion_ids:-> motion/category_id +FIELD 1rR: => motion_category/meeting_id:-> meeting/ -SQL nt:1t => motion_block/motion_ids:-> motion/block_id -SQL 1t:1GtR => motion_block/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GtR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GtR => motion_block/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => motion_block/meeting_id:-> meeting/motion_block_ids +SQL nt:1r => motion_block/motion_ids:-> motion/block_id +SQL 1t:1GrR => motion_block/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => motion_block/projection_ids:-> projection/content_object_id +FIELD 1rR: => motion_block/meeting_id:-> meeting/ -FIELD 1tR:nt => motion_change_recommendation/motion_id:-> motion/change_recommendation_ids -FIELD 1tR:nt => motion_change_recommendation/meeting_id:-> meeting/motion_change_recommendation_ids +FIELD 1rR: => motion_change_recommendation/motion_id:-> motion/ +FIELD 1rR: => motion_change_recommendation/meeting_id:-> meeting/ -FIELD 1t:nt => motion_state/submitter_withdraw_state_id:-> motion_state/submitter_withdraw_back_ids -SQL nt:1t => motion_state/submitter_withdraw_back_ids:-> motion_state/submitter_withdraw_state_id +FIELD 1r: => motion_state/submitter_withdraw_state_id:-> motion_state/ +SQL nt:1r => motion_state/submitter_withdraw_back_ids:-> motion_state/submitter_withdraw_state_id SQL nt:nt => motion_state/next_state_ids:-> motion_state/previous_state_ids SQL nt:nt => motion_state/previous_state_ids:-> motion_state/next_state_ids -SQL nt:1tR => motion_state/motion_ids:-> motion/state_id -SQL nt:1t => motion_state/motion_recommendation_ids:-> motion/recommendation_id -FIELD 1tR:nt => motion_state/workflow_id:-> motion_workflow/state_ids -SQL 1t:1tR => motion_state/first_state_of_workflow_id:-> motion_workflow/first_state_id -FIELD 1tR:nt => motion_state/meeting_id:-> meeting/motion_state_ids - -SQL nt:1tR => motion_workflow/state_ids:-> motion_state/workflow_id -FIELD 1tR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id -SQL 1t:1tR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id -SQL 1t:1tR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id -SQL 1t:1tR => motion_workflow/default_statute_amendment_workflow_meeting_id:-> meeting/motions_default_statute_amendment_workflow_id -FIELD 1tR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids - -SQL nt:1t => motion_statute_paragraph/motion_ids:-> motion/statute_paragraph_id -FIELD 1tR:nt => motion_statute_paragraph/meeting_id:-> meeting/motion_statute_paragraph_ids - -FIELD 1GtR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids -SQL nt:1t => poll/option_ids:-> option/poll_id +SQL nt:1rR => motion_state/motion_ids:-> motion/state_id +SQL nt:1r => motion_state/motion_recommendation_ids:-> motion/recommendation_id +FIELD 1rR: => motion_state/workflow_id:-> motion_workflow/ +SQL 1t:1rR => motion_state/first_state_of_workflow_id:-> motion_workflow/first_state_id +FIELD 1rR: => motion_state/meeting_id:-> meeting/ + +SQL nt:1rR => motion_workflow/state_ids:-> motion_state/workflow_id +FIELD 1rR: => motion_workflow/first_state_id:-> motion_state/ +SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id +SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id +SQL 1t:1rR => motion_workflow/default_statute_amendment_workflow_meeting_id:-> meeting/motions_default_statute_amendment_workflow_id +FIELD 1rR: => motion_workflow/meeting_id:-> meeting/ + +SQL nt:1r => motion_statute_paragraph/motion_ids:-> motion/statute_paragraph_id +FIELD 1rR: => motion_statute_paragraph/meeting_id:-> meeting/ + +FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ +SQL nt:1r => poll/option_ids:-> option/poll_id FIELD 1r: => poll/global_option_id:-> option/ SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids -SQL nt:1GtR => poll/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => poll/meeting_id:-> meeting/poll_ids +SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id +FIELD 1rR: => poll/meeting_id:-> meeting/ -FIELD 1t:nt => option/poll_id:-> poll/option_ids +FIELD 1r: => option/poll_id:-> poll/ SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id -SQL nt:1tR => option/vote_ids:-> vote/option_id -FIELD 1Gt:nt,nt,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id -FIELD 1tR:nt => option/meeting_id:-> meeting/option_ids - -FIELD 1tR:nt => vote/option_id:-> option/vote_ids -FIELD 1t:nt => vote/user_id:-> user/vote_ids -FIELD 1t:nt => vote/delegated_user_id:-> user/delegated_vote_ids -FIELD 1tR:nt => vote/meeting_id:-> meeting/vote_ids - -SQL nt:1tR => assignment/candidate_ids:-> assignment_candidate/assignment_id -SQL nt:1GtR => assignment/poll_ids:-> poll/content_object_id -SQL 1t:1GtR => assignment/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GtR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1rR => option/vote_ids:-> vote/option_id +FIELD 1Gr:,, => option/content_object_id:-> motion/,user/,poll_candidate_list/ +FIELD 1rR: => option/meeting_id:-> meeting/ + +FIELD 1rR: => vote/option_id:-> option/ +FIELD 1r: => vote/user_id:-> user/ +FIELD 1r: => vote/delegated_user_id:-> user/ +FIELD 1rR: => vote/meeting_id:-> meeting/ + +SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id +SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id +SQL 1t:1GrR => assignment/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids SQL nt:nGt => assignment/attachment_ids:-> mediafile/attachment_ids -SQL nt:1GtR => assignment/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => assignment/meeting_id:-> meeting/assignment_ids +SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id +FIELD 1rR: => assignment/meeting_id:-> meeting/ -FIELD 1tR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids -FIELD 1t:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids -FIELD 1tR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids +FIELD 1rR: => assignment_candidate/assignment_id:-> assignment/ +FIELD 1r: => assignment_candidate/meeting_user_id:-> meeting_user/ +FIELD 1rR: => assignment_candidate/meeting_id:-> meeting/ -SQL nt:1tR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id -FIELD 1tR:nt => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids -SQL 1tR:1Gt => poll_candidate_list/option_id:-> option/content_object_id +SQL nt:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id +FIELD 1rR: => poll_candidate_list/meeting_id:-> meeting/ +SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id -FIELD 1tR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids -FIELD 1t:nt => poll_candidate/user_id:-> user/poll_candidate_ids -FIELD 1tR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids +FIELD 1rR: => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/ +FIELD 1r: => poll_candidate/user_id:-> user/ +FIELD 1rR: => poll_candidate/meeting_id:-> meeting/ SQL nt:nt => mediafile/inherited_access_group_ids:-> group/mediafile_inherited_access_group_ids SQL nt:nt => mediafile/access_group_ids:-> group/mediafile_access_group_ids -FIELD 1t:nt => mediafile/parent_id:-> mediafile/child_ids -SQL nt:1t => mediafile/child_ids:-> mediafile/parent_id -SQL 1t:1GtR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GtR => mediafile/projection_ids:-> projection/content_object_id -FIELD nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids -FIELD 1GtR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids +FIELD 1r: => mediafile/parent_id:-> mediafile/ +SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id +SQL 1t:1GrR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => mediafile/projection_ids:-> projection/content_object_id +SQL nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids +FIELD 1GrR:, => mediafile/owner_id:-> meeting/,organization/ SQL 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id SQL 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id SQL 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id @@ -2638,48 +2638,48 @@ SQL 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting SQL 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id SQL 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id -SQL nt:1t => projector/current_projection_ids:-> projection/current_projector_id -SQL nt:1t => projector/preview_projection_ids:-> projection/preview_projector_id -SQL nt:1t => projector/history_projection_ids:-> projection/history_projector_id -SQL 1t:1tR => projector/used_as_reference_projector_meeting_id:-> meeting/reference_projector_id -FIELD 1t:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/default_projector_current_list_of_speakers_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/default_projector_assignment_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/default_projector_mediafile_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/default_projector_message_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/default_projector_countdown_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/default_projector_assignment_poll_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/default_projector_motion_poll_ids -FIELD 1t:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids -FIELD 1tR:nt => projector/meeting_id:-> meeting/projector_ids - -FIELD 1t:nt => projection/current_projector_id:-> projector/current_projection_ids -FIELD 1t:nt => projection/preview_projector_id:-> projector/preview_projection_ids -FIELD 1t:nt => projection/history_projector_id:-> projector/history_projection_ids -FIELD 1GtR:nt,nt,nt,nt,nt,nt,nt,nt,nt,nt,nt => projection/content_object_id:-> meeting/projection_ids,motion/projection_ids,mediafile/projection_ids,list_of_speakers/projection_ids,motion_block/projection_ids,assignment/projection_ids,agenda_item/projection_ids,topic/projection_ids,poll/projection_ids,projector_message/projection_ids,projector_countdown/projection_ids -FIELD 1tR:nt => projection/meeting_id:-> meeting/all_projection_ids - -SQL nt:1GtR => projector_message/projection_ids:-> projection/content_object_id -FIELD 1tR:nt => projector_message/meeting_id:-> meeting/projector_message_ids - -SQL nt:1GtR => projector_countdown/projection_ids:-> projection/content_object_id +SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id +SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id +SQL nt:1r => projector/history_projection_ids:-> projection/history_projector_id +SQL 1t:1rR => projector/used_as_reference_projector_meeting_id:-> meeting/reference_projector_id +FIELD 1r: => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/ +FIELD 1r: => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/ +FIELD 1rR: => projector/meeting_id:-> meeting/ + +FIELD 1r: => projection/current_projector_id:-> projector/ +FIELD 1r: => projection/preview_projector_id:-> projector/ +FIELD 1r: => projection/history_projector_id:-> projector/ +FIELD 1GrR:,,,,,,,,,, => projection/content_object_id:-> meeting/,motion/,mediafile/,list_of_speakers/,motion_block/,assignment/,agenda_item/,topic/,poll/,projector_message/,projector_countdown/ +FIELD 1rR: => projection/meeting_id:-> meeting/ + +SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id +FIELD 1rR: => projector_message/meeting_id:-> meeting/ + +SQL nt:1GrR => projector_countdown/projection_ids:-> projection/content_object_id SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id -FIELD 1tR:nt => projector_countdown/meeting_id:-> meeting/projector_countdown_ids +FIELD 1rR: => projector_countdown/meeting_id:-> meeting/ -SQL nt:1tR => chat_group/chat_message_ids:-> chat_message/chat_group_id +SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids -FIELD 1tR:nt => chat_group/meeting_id:-> meeting/chat_group_ids +FIELD 1rR: => chat_group/meeting_id:-> meeting/ -FIELD 1tR:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids -FIELD 1tR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids -FIELD 1tR:nt => chat_message/meeting_id:-> meeting/chat_message_ids +FIELD 1rR: => chat_message/meeting_user_id:-> meeting_user/ +FIELD 1rR: => chat_message/chat_group_id:-> chat_group/ +FIELD 1rR: => chat_message/meeting_id:-> meeting/ */ diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index bf5ae2d0..af2e56bb 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -48,6 +48,7 @@ class FIELD_SQL_ERROR_ENUM(Enum): SQL = 2 ERROR = 3 + class SubstDict(TypedDict, total=False): """dict for substitutions of field templates""" @@ -97,7 +98,8 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str "read_only", "enum", "items", - "to", # will be used for creating view-fields, but also replacement for fk-reference to id + "to", + "reference", # "on_delete", # must have other name then the key-value-store one # "sql" # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? @@ -242,54 +244,45 @@ def get_relation_type( fdata.get("to"), fdata.get("reference") ) ) - final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = Helper.check_relation_definitions( own_table_field, [foreign_table_field] ) - if not error: - if result := Helper.generate_field_view_or_nothing( - own_table_field, foreign_table_field - ): - text.update( - cls.get_schema_simple_types(table_name, fname, fdata, "number") - ) - initially_deferred = fdata.get( - "deferred" - ) or ModelsHelper.is_fk_initially_deferred( - table_name, foreign_table_field.table + if state == FIELD_SQL_ERROR_ENUM.FIELD: + text.update(cls.get_schema_simple_types(table_name, fname, fdata, "number")) + initially_deferred = fdata.get( + "deferred" + ) or ModelsHelper.is_fk_initially_deferred( + table_name, foreign_table_field.table + ) + text["alter_table_final"] = ( + Helper.get_foreign_key_table_constraint_as_alter_table( + table_name, + foreign_table_field.table, + fname, + foreign_table_field.ref_column, + initially_deferred, ) - text["alter_table_final"] = ( - Helper.get_foreign_key_table_constraint_as_alter_table( - table_name, - foreign_table_field.table, - fname, - foreign_table_field.ref_column, - initially_deferred, - ) + ) + elif state == FIELD_SQL_ERROR_ENUM.SQL: + if sql := fdata.get("sql", ""): + text["view"] = sql + ",\n" + elif foreign_table_field.field_def["type"] == "generic-relation": + text["view"] = cls.get_sql_for_relation_1_1( + table_name, + fname, + foreign_table_field.ref_column, + foreign_table_field.table, + f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}", ) - final_info = "FIELD " + final_info - elif result is False: - if sql := fdata.get("sql", ""): - text["view"] = sql + ",\n" - elif foreign_table_field.field_def["type"] == "generic-relation": - text["view"] = cls.get_sql_for_relation_1_1( - table_name, - fname, - foreign_table_field.ref_column, - foreign_table_field.table, - f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}", - ) - else: - text["view"] = cls.get_sql_for_relation_1_1( - table_name, - fname, - foreign_table_field.ref_column, - foreign_table_field.table, - cast(str, foreign_table_field.column), - ) - final_info = "SQL " + final_info else: - final_info = "NOTHING " + final_info + text["view"] = cls.get_sql_for_relation_1_1( + table_name, + fname, + foreign_table_field.ref_column, + foreign_table_field.table, + cast(str, foreign_table_field.column), + ) text["final_info"] = final_info return text @@ -320,14 +313,12 @@ def get_relation_list_type( fdata.get("reference"), ) ) - final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = Helper.check_relation_definitions( own_table_field, [foreign_table_field] ) - if not error: - if Helper.generate_field_view_or_nothing( - own_table_field, foreign_table_field - ): + if state != FIELD_SQL_ERROR_ENUM.ERROR: + if primary: if foreign_table_field.field_def.get("type") == "relation-list": nm_table_name, value = Helper.get_nm_table_for_n_m_relation_lists( own_table_field, foreign_table_field @@ -340,7 +331,6 @@ def get_relation_list_type( ) if sql := fdata.get("sql", ""): text["view"] = sql + ",\n" - final_info = "SQL " + final_info else: foreign_table_column = cast(str, foreign_table_field.column) foreign_table_field_ref_id = cast(str, foreign_table_field.ref_column) @@ -407,7 +397,6 @@ def get_relation_list_type( foreign_table_field.column, ) ) - final_info = "SQL " + final_info text["final_info"] = final_info return text @@ -459,21 +448,11 @@ def get_generic_relation_type( ) ) - error = False - final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = Helper.check_relation_definitions( own_table_field, foreign_table_fields ) - if not error: - if not all( - Helper.generate_field_view_or_nothing( - own_table_field, foreign_table_field - ) - for foreign_table_field in foreign_table_fields - ): - raise Exception( - f"Error in generation for collectionfield '{own_table_field.collectionfield}'" - ) + if state == FIELD_SQL_ERROR_ENUM.FIELD: text.update( cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"]) ) @@ -504,7 +483,6 @@ def get_generic_relation_type( text["table"] += Helper.get_generic_field_constraint( own_table_field.column, foreign_tables ) - final_info = "FIELD " + final_info text["final_info"] = final_info return text @@ -519,22 +497,11 @@ def get_generic_relation_list_type( table_name, fname, fdata.get("to"), fdata.get("reference") ) ) - error = False - final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = Helper.check_relation_definitions( own_table_field, foreign_table_fields ) - if not error: - if not all( - Helper.generate_field_view_or_nothing( - own_table_field, foreign_table_field - ) - and foreign_table_field.field_def["type"] == "relation-list" - for foreign_table_field in foreign_table_fields - ): - raise Exception( - f"Error in generation for collectfield '{own_table_field.collectionfield}'" - ) + if state == FIELD_SQL_ERROR_ENUM.SQL and primary: # create gm-intermediate table gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( own_table_field, foreign_table_fields @@ -569,7 +536,6 @@ def get_generic_relation_list_type( # foreign_table_field.ref_column, # initially_deferred, # ) - final_info = "FIELD " + final_info text["final_info"] = final_info return text @@ -687,7 +653,6 @@ class Helper: Generated: What will be generated for left field FIELD: a usual Database field SQL: a sql-expression in a view - NOTHING: still nothing ***: Error Field Attributes:Field Attributes opposite side 1: cardinality 1 @@ -696,8 +661,7 @@ class Helper: nG: cardinality n with generic-relation-list field t: "to" defined r: "reference" defined - s: sql directive given, but must be generated - s+: sql directive inclusive sql-statement + s: sql directive inclusive sql-statement R: Required Model.Field -> Model.Field model.field names @@ -811,26 +775,6 @@ def get_on_action_mode(action: str, delete: bool) -> str: raise Exception(f"{action} is not a valid action mode") return "" - @staticmethod - def get_foreign_key_table_column( - to: str | None, reference: str | None - ) -> tuple[str, str]: - if reference: - result = InternalHelper.ref_compiled.search(reference) - if result is None: - return reference.strip(), "id" - re_groups = result.groups() - cols = re_groups[1] - if cols: - cols = ",".join([col.strip() for col in cols.split(",")]) - else: - cols = "id" - return re_groups[0], cols - elif to: - return to.split("/")[0], "id" - else: - raise Exception("Relation field without reference or to") - @staticmethod def get_nm_table_for_n_m_relation_lists( own_table_field: TableFieldType, foreign_table_field: TableFieldType @@ -957,25 +901,31 @@ def get_initials( return subst, text @staticmethod - def get_cardinality(field_all: TableFieldType) -> tuple[str, bool]: + def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: """ - Returns string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) + Returns + - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) + - string with error message or empty string if no error + """ + error = "" field = field_all.field_def if field: required = bool(field.get("required")) sql = "sql" in field - sql_empty = field.get("sql") == "" to = bool(field.get("to")) reference = bool(field.get("reference")) # general rules of inconsistent field descriptions on field level - error = ( - (required and sql) - or (required and not (to or reference)) - or (not required and sql_empty and not to) - or not (required or sql or to or reference) - ) + if field.get("sql") == "": + error = "sql attribute may not be empty" + elif required and sql: + error = "Field with attribute sql cannot be required" + elif not (to or reference): + error = "Relation field must have `to` or `reference` attribut set" + elif field["type"] == "generic-relation-list" and required: + error = "generic-relation-list cannot be required: not implemented" + if field["type"] == "relation": result = "1" elif field["type"] == "relation-list": @@ -983,8 +933,6 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, bool]: elif field["type"] == "generic-relation": result = "1G" elif field["type"] == "generic-relation-list": - if field.get("required"): - error = True result = "nG" else: raise Exception( @@ -992,25 +940,22 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, bool]: ) if reference: result += "r" - if to: + if ( + to and not reference + ): # to with reference only for temporaray backup compatibility in backend relation-handling result += "t" - if sql: - result += "s" - if field["sql"]: - result += "+" - else: - result += "-" if required: result += "R" + if sql: + result += "s" else: result = "" - error = False return result, error @staticmethod def check_relation_definitions( own_field: TableFieldType, foreign_fields: list[TableFieldType] - ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str, str|None]: + ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str, str]: """ Decides for the own-field, - whether it is a field, a sql-expression or is there an error @@ -1021,13 +966,12 @@ def check_relation_definitions( case of an error an error text Returns: - - field, sql, error => enum + - field, sql, error => enum FIELD_SQL_ERROR_ENUM - primary field (only relevant for list fields) - complete relational text with FIELD, SQL or *** in front - - error line if error + - error line if error else empty string """ - error = False - text = "" + error = "" own_c, tmp_error = Helper.get_cardinality(own_field) error = error or tmp_error foreigns_c = [] @@ -1036,97 +980,96 @@ def check_relation_definitions( foreign_c, tmp_error = Helper.get_cardinality(foreign_field) if own_c == "1tR" and foreign_c == "1r": raise Exception( - f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1tR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut." + f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1rR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut." ) foreigns_c.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) - # todo: generate_field_view_or_nothing hier einfügen, Rückgabe feld oder select - # und bei listen auch primär, sekundär - # Fehlertext hier auch erzeugen (2 zeiig in relliste) if error: - text = "*** " - text += f"{own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" - return text, error + state = FIELD_SQL_ERROR_ENUM.ERROR + else: + for i, foreign_field in enumerate(foreign_fields): + if i == 0: + state, primary, error = Helper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) + else: + statex, primaryx, error = Helper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) + if not error and (statex != state or primaryx != primary): + error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" + if error: + state = FIELD_SQL_ERROR_ENUM.ERROR + break + + state_text = "***" if state == FIELD_SQL_ERROR_ENUM.ERROR else state.name + text = f"{state_text} {own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" + if state == FIELD_SQL_ERROR_ENUM.ERROR: + text += f" {error}" + return state, primary, text, error @staticmethod - def generate_field_view_or_nothing( - own: TableFieldType, foreign: TableFieldType - ) -> bool: + def generate_field_or_sql_decision( + own: TableFieldType, own_c: str, foreign: TableFieldType, foreign_c: str + ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str]: """ - Decides for the own-field - - relation-list and generic-relation-list are always sql-expressions. - True significates, that it is the pimary that creates the intermediate table - - for relation and generic-relation True marks a field, False a sql-expression + Returns: + - field, sql, error for own => enum FIELD_SQL_ERROR_ENUM + - primary field for own: (only relevant for list fields) + - error line if error else empty string """ - decision_list = { - ("relation", "relation"): "decide_primary_side", - ("relation", "relation-list"): True, - ("relation", "generic-relation"): False, - ("relation", "generic-relation-list"): "not implemented", - ("relation", None): True, - ("relation-list", "relation"): False, - ("relation-list", "relation-list"): "decide_alphabetical", - ("relation-list", "generic-relation"): False, - ("relation-list", "generic-relation-list"): False, - ("relation-list", None): "decide_sql", - ("generic-relation", "relation"): True, - ("generic-relation", "relation-list"): True, - ("generic-relation", "generic-relation"): "not implemented", - ("generic-relation", "generic-relation-list"): "not implemented", - ("generic-relation", None): True, - ("generic-relation-list", "relation"): "not implemented", - ("generic-relation-list", "relation-list"): True, - ("generic-relation-list", "generic-relation"): "not implemented", - ("generic-relation-list", "generic-relation-list"): "not implemented", - ("generic-relation-list", None): "not implemented", + decision_list: dict[ + tuple[str, str], tuple[FIELD_SQL_ERROR_ENUM | None, bool | str | None] + ] = { + ("1Gr", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1Gr", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1Gr", "1tR"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1GrR", "1t"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1GrR", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1GrR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1r", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1rR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1t", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1t", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1t", "1rR"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1r", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1r", "ntR"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1tR", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1tR", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1rR", "1t"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("1rR", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), + ("nGt", "nt"): (FIELD_SQL_ERROR_ENUM.SQL, True), + ("nr", ""): (FIELD_SQL_ERROR_ENUM.SQL, True), + ("nt", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("nt", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("nt", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("nt", "1rR"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("nt", "nGt"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("nt", "nt"): (FIELD_SQL_ERROR_ENUM.SQL, "primary_decide_alphabetical"), + ("ntR", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), } - result = decision_list[ - ( - own_type := own.field_def.get("type", ""), - foreign_type := ( - foreign.field_def.get("type") if foreign.field_def else None - ), - ) - ] - if result == "not implemented": - raise Exception( - f"Type combination not implemented: {own_type}:{foreign_type} on field {own.collectionfield}" - ) - elif result == "decide_primary_side": - #TODO: mehr auf fehler checken - if own.field_def.get("required", False) == foreign.field_def.get( - "required", False - ): - if bool(own.field_def.get("sql", False)) == bool( - foreign.field_def.get("sql", False) - ): - if bool(own.field_def.get("reference", False)) == bool( - foreign.field_def.get("reference", False) - ): - raise Exception( - f"Type combination undecidable: {own_type}:{foreign_type} on field {own.collectionfield}. Give field or reverse field a 'reference' Attribut, if you want the value to be settable on this field" - ) - else: - return bool(own.field_def.get("reference", False)) - else: - return bool(foreign.field_def.get("sql", False)) - else: - return own.field_def.get("required", False) - elif result == "decide_alphabetical": - return ( + + state: FIELD_SQL_ERROR_ENUM | str | None + primary: bool | str | None + error = "" + + state, primary = decision_list.get( + (own_c.rstrip("s"), foreign_c.rstrip("s")), (None, None) + ) + if state is None: + error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" + state = FIELD_SQL_ERROR_ENUM.ERROR + elif primary == "primary_decide_alphabetical": + if own.collectionfield == foreign.collectionfield: + error = f"Field {own.collectionfield} identical with foreign.collectionfield. SQL_decice_alphabetical uncedidable!\n" + state = FIELD_SQL_ERROR_ENUM.ERROR + primary = ( foreign.collectionfield == "-" or own.collectionfield < foreign.collectionfield ) - elif result == "decide_sql": - if own.field_def.get("sql") or own.field_def.get("reference"): - return True - else: - raise Exception( - f"Missing sql-or to-attribute for field {own.collectionfield}" - ) - return cast(bool, result) + return cast(FIELD_SQL_ERROR_ENUM, state), cast(bool, primary), error @staticmethod def get_generic_combined_fields( @@ -1151,7 +1094,7 @@ def is_fk_initially_deferred(own_table: str, foreign_table: str) -> bool: def _first_to_second(t1: str, t2: str) -> bool: for field in MODELS[t1].values(): if field.get("required") and field["type"].startswith("relation"): - ftable, _ = InternalHelper.get_foreign_key_table_column( + ftable = ModelsHelper.get_foreign_table_from_to_or_reference( field.get("to"), field.get("reference") ) if ftable == t2: @@ -1162,6 +1105,21 @@ def _first_to_second(t1: str, t2: str) -> bool: return _first_to_second(foreign_table, own_table) return False + @staticmethod + def get_foreign_table_from_to_or_reference( + to: str | None, reference: str | None + ) -> str: + if reference: + result = InternalHelper.ref_compiled.search(reference) + if result is None: + return reference.strip() + re_groups = result.groups() + return re_groups[0] + elif to: + return to.split("/")[0] + else: + raise Exception("Relation field without reference or to") + @staticmethod def get_definitions_from_foreign_list( table: str, @@ -1172,12 +1130,17 @@ def get_definitions_from_foreign_list( """ used for generic_relation with multiple foreign relations """ - if to and reference: - raise Exception( - f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" - ) + # temporarely allowed + # if to and reference: + # raise Exception( + # f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" + # ) results: list[TableFieldType] = [] - if isinstance(to, dict): + # precedence for reference + if reference: + for ref in reference: + results.append(TableFieldType.get_definitions_from_foreign(None, ref)) + elif isinstance(to, dict): fname = "/" + to["field"] for table in to["collections"]: results.append( @@ -1188,9 +1151,6 @@ def get_definitions_from_foreign_list( results.append( TableFieldType.get_definitions_from_foreign(collectionfield, None) ) - elif reference: - for ref in reference: - results.append(TableFieldType.get_definitions_from_foreign(None, ref)) return results diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index f684ec89..4ccba1c5 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -38,13 +38,11 @@ def get_definitions_from_foreign( fname = "" tfield: dict[str, Any] = {} ref_column = "" - if to: + if reference: + tname, ref_column = InternalHelper.get_foreign_key_table_column(reference) + elif to: tname, fname, tfield = InternalHelper.get_field_definition_from_to(to) ref_column = "id" - if reference: - tname, ref_column = InternalHelper.get_foreign_key_table_column( - to, reference - ) return TableFieldType(tname, fname, tfield, ref_column) @@ -243,11 +241,9 @@ def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: return tname, fname, field @staticmethod - def get_foreign_key_table_column( - to: str | None, reference: str | None - ) -> tuple[str, str]: + def get_foreign_key_table_column(reference: str | None) -> tuple[str, str]: """ - Returns a tuple (table_name, field_name) gotten from "to" and/or "reference"-attribut + Returns a tuple (table_name, field_name) gotten from "reference"-attribut """ if reference: result = InternalHelper.ref_compiled.search(reference) @@ -260,10 +256,8 @@ def get_foreign_key_table_column( else: cols = "id" return re_groups[0], cols - elif to: - return to.split("/")[0], "id" else: - raise Exception("Relation field without reference or to") + raise Exception("Relation field without reference") @classmethod def get_models(cls, collection: str, field: str) -> dict[str, Any]: diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 362c3e28..4243dc11 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -37,6 +37,15 @@ def test_one_to_one_pre_populated_1r_1t(self) -> None: meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_meeting_for_committee_id"]) assert meeting_row["default_meeting_for_committee_id"] == self.committee1_id + # TODO: remove test, fiktiv test with 1r:1tR, test s.o, in der die meeting-Seite ein SQL hat + # jetzt setze ich mal ein erequired auf der Meeting Seite. Was ist übrigens mit 1r:1rR + def test_one_to_one_pre_populated_1r_1tR(self) -> None: + with self.db_connection.cursor() as curs: + committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["default_meeting_id"]) + assert committee_row["default_meeting_id"] == self.meeting1_id + meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_meeting_for_committee_id"]) + assert meeting_row["default_meeting_for_committee_id"] == self.committee1_id + def test_one_to_one_1tR_1t(self) -> None: with self.db_connection.cursor() as curs: # Prepopulated diff --git a/models.yml b/models.yml index 06d185de..b3910d20 100644 --- a/models.yml +++ b/models.yml @@ -23,10 +23,10 @@ # In this case the `to` value is only used for historical reason, onky the `reference` property is used # for the schema generation. # If necessary the sql can be written manually, see `sql`. -# `reference: Date: Wed, 3 Apr 2024 16:03:49 +0200 Subject: [PATCH 035/142] add check for existing to-attribute if reference is present --- dev/sql/schema_relational.sql | 229 +++++++++++++++------------------ dev/src/generate_sql_schema.py | 17 ++- 2 files changed, 109 insertions(+), 137 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index a3c928b8..2d3f3464 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -457,7 +457,6 @@ CREATE TABLE IF NOT EXISTS organizationT ( saml_metadata_idp text, saml_metadata_sp text, saml_private_key text, - theme_id integer NOT NULL, users_email_sender varchar(256) DEFAULT 'OpenSlides', users_email_replyto varchar(256), users_email_subject varchar(256) DEFAULT 'OpenSlides access data', @@ -600,7 +599,6 @@ CREATE TABLE IF NOT EXISTS committeeT ( name varchar(256) NOT NULL, description text, external_id varchar(256), - default_meeting_id integer, user_ids integer[], forwarding_user_id integer ); @@ -766,29 +764,10 @@ This email was generated automatically.', poll_default_onehundred_percent_base enum_meeting_poll_default_onehundred_percent_base DEFAULT 'YNA', poll_default_backend enum_meeting_poll_default_backend DEFAULT 'fast', poll_couple_countdown boolean DEFAULT True, - logo_projector_main_id integer, - logo_projector_header_id integer, - logo_web_header_id integer, - logo_pdf_header_l_id integer, - logo_pdf_header_r_id integer, - logo_pdf_footer_l_id integer, - logo_pdf_footer_r_id integer, - logo_pdf_ballot_paper_id integer, - font_regular_id integer, - font_italic_id integer, - font_bold_id integer, - font_bold_italic_id integer, - font_monospace_id integer, - font_chyron_speaker_name_id integer, - font_projector_h1_id integer, - font_projector_h2_id integer, committee_id integer NOT NULL, user_ids integer[], reference_projector_id integer NOT NULL, - list_of_speakers_countdown_id integer, - poll_countdown_id integer, - default_group_id integer NOT NULL, - admin_group_id integer + default_group_id integer NOT NULL ); @@ -1197,7 +1176,6 @@ CREATE TABLE IF NOT EXISTS pollT ( content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), - global_option_id integer, meeting_id integer NOT NULL ); @@ -1617,14 +1595,10 @@ CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_groupT ( -- View definitions CREATE OR REPLACE VIEW organization AS SELECT *, -(select array_agg(c.id) from committeeT c) as committee_ids, (select array_agg(m.id) from meetingT m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meetingT m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, (select array_agg(m.id) from meetingT m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(ot.id) from organization_tagT ot) as organization_tag_ids, -(select array_agg(t.id) from themeT t) as theme_ids, -(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, -(select array_agg(u.id) from userT u) as user_ids +(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids FROM organizationT o; @@ -1661,11 +1635,6 @@ CREATE OR REPLACE VIEW organization_tag AS SELECT *, FROM organization_tagT o; -CREATE OR REPLACE VIEW theme AS SELECT *, -(select o.id from organizationT o where o.theme_id = t.id) as theme_for_organization_id -FROM themeT t; - - CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(m.id) from meetingT m where m.committee_id = c.id) as meeting_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_userT n where n.committee_id = c.id) as manager_ids, @@ -1718,7 +1687,6 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(c.id) from chat_groupT c where c.meeting_id = m.id) as chat_group_ids, (select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, (select array_agg(s.id) from structure_levelT s where s.meeting_id = m.id) as structure_level_ids, -(select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, (select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids, @@ -1748,7 +1716,6 @@ FROM structure_levelT s; CREATE OR REPLACE VIEW group_ AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_userT n where n.group_id = g.id) as meeting_user_ids, (select m.id from meetingT m where m.default_group_id = g.id) as default_group_for_meeting_id, -(select m.id from meetingT m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_access_group_ids, (select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_inherited_access_group_ids, (select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as read_comment_section_ids, @@ -1877,7 +1844,6 @@ FROM pollT p; CREATE OR REPLACE VIEW option AS SELECT *, -(select p.id from pollT p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, (select array_agg(v.id) from voteT v where v.option_id = o.id) as vote_ids FROM optionT o; @@ -1905,23 +1871,7 @@ CREATE OR REPLACE VIEW mediafile AS SELECT *, (select array_agg(m1.id) from mediafileT m1 where m1.parent_id = m.id) as child_ids, (select l.id from list_of_speakersT l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, (select array_agg(p.id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids, -(select m1.id from meetingT m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id +(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids FROM mediafileT m; @@ -1939,9 +1889,7 @@ FROM projector_messageT p; CREATE OR REPLACE VIEW projector_countdown AS SELECT *, -(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, -(select m.id from meetingT m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, -(select m.id from meetingT m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id +(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids FROM projector_countdownT p; @@ -1952,13 +1900,10 @@ CREATE OR REPLACE VIEW chat_group AS SELECT *, FROM chat_groupT c; -- Alter table relations -ALTER TABLE organizationT ADD FOREIGN KEY(theme_id) REFERENCES themeT(id); - ALTER TABLE meeting_userT ADD FOREIGN KEY(user_id) REFERENCES userT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_userT(id); -ALTER TABLE committeeT ADD FOREIGN KEY(default_meeting_id) REFERENCES meetingT(id); ALTER TABLE committeeT ADD FOREIGN KEY(forwarding_user_id) REFERENCES userT(id); ALTER TABLE meetingT ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organizationT(id); @@ -1967,28 +1912,9 @@ ALTER TABLE meetingT ADD FOREIGN KEY(template_for_organization_id) REFERENCES or ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_regular_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_italic_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafileT(id); ALTER TABLE meetingT ADD FOREIGN KEY(committee_id) REFERENCES committeeT(id); ALTER TABLE meetingT ADD FOREIGN KEY(reference_projector_id) REFERENCES projectorT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdownT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdownT(id); ALTER TABLE meetingT ADD FOREIGN KEY(default_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(admin_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; ALTER TABLE structure_levelT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); @@ -2081,7 +2007,6 @@ ALTER TABLE motion_statute_paragraphT ADD FOREIGN KEY(meeting_id) REFERENCES mee ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); -ALTER TABLE pollT ADD FOREIGN KEY(global_option_id) REFERENCES optionT(id); ALTER TABLE pollT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE optionT ADD FOREIGN KEY(poll_id) REFERENCES pollT(id); @@ -2287,15 +2212,20 @@ Model.Field -> Model.Field */ /* -SQL nrs: => organization/committee_ids:-> committee/ +*** nrs: => organization/committee_ids:-> committee/ + Field with reference temporarely needs also to-attribute SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id -SQL nr: => organization/organization_tag_ids:-> organization_tag/ -FIELD 1rR: => organization/theme_id:-> theme/ -SQL nr: => organization/theme_ids:-> theme/ +*** nr: => organization/organization_tag_ids:-> organization_tag/ + Field with reference temporarely needs also to-attribute +*** 1rR: => organization/theme_id:-> theme/ + Field with reference temporarely needs also to-attribute +*** nr: => organization/theme_ids:-> theme/ + Field with reference temporarely needs also to-attribute SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id -SQL nr: => organization/user_ids:-> user/ +*** nr: => organization/user_ids:-> user/ + Field with reference temporarely needs also to-attribute SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids @@ -2324,10 +2254,12 @@ SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_id SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids -SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id +*** 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id + Field with reference temporarely needs also to-attribute SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id -FIELD 1r: => committee/default_meeting_id:-> meeting/ +*** 1r: => committee/default_meeting_id:-> meeting/ + Field with reference temporarely needs also to-attribute SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids @@ -2382,29 +2314,48 @@ SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1rR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1rR => meeting/chat_message_ids:-> chat_message/meeting_id SQL nt:1rR => meeting/structure_level_ids:-> structure_level/meeting_id -FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ -FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ -FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ -FIELD 1r: => meeting/font_regular_id:-> mediafile/ -FIELD 1r: => meeting/font_italic_id:-> mediafile/ -FIELD 1r: => meeting/font_bold_id:-> mediafile/ -FIELD 1r: => meeting/font_bold_italic_id:-> mediafile/ -FIELD 1r: => meeting/font_monospace_id:-> mediafile/ -FIELD 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ -FIELD 1r: => meeting/font_projector_h1_id:-> mediafile/ -FIELD 1r: => meeting/font_projector_h2_id:-> mediafile/ +*** 1r: => meeting/logo_projector_main_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_projector_header_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_web_header_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_regular_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_italic_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_bold_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_bold_italic_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_monospace_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_projector_h1_id:-> mediafile/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/font_projector_h2_id:-> mediafile/ + Field with reference temporarely needs also to-attribute FIELD 1rR: => meeting/committee_id:-> committee/ -SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id +*** 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id + Field with reference temporarely needs also to-attribute SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids FIELD 1rR: => meeting/reference_projector_id:-> projector/ -FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ -FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ +*** 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ + Field with reference temporarely needs also to-attribute +*** 1r: => meeting/poll_countdown_id:-> projector_countdown/ + Field with reference temporarely needs also to-attribute SQL nt:1GrR => meeting/projection_ids:-> projection/content_object_id SQL ntR:1r => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id SQL ntR:1r => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id @@ -2421,7 +2372,8 @@ SQL ntR:1r => meeting/default_projector_assignment_poll_ids:-> projector/used_as SQL ntR:1r => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id FIELD 1rR: => meeting/default_group_id:-> group/ -FIELD 1r: => meeting/admin_group_id:-> group/ +*** 1r: => meeting/admin_group_id:-> group/ + Field with reference temporarely needs also to-attribute SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id @@ -2429,7 +2381,8 @@ FIELD 1rR: => structure_level/meeting_id:-> meeting/ SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id -SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id +*** 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id + Field with reference temporarely needs also to-attribute SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids @@ -2575,14 +2528,16 @@ FIELD 1rR: => motion_statute_paragraph/meeting_id:-> meeting/ FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ SQL nt:1r => poll/option_ids:-> option/poll_id -FIELD 1r: => poll/global_option_id:-> option/ +*** 1r: => poll/global_option_id:-> option/ + Field with reference temporarely needs also to-attribute SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id FIELD 1rR: => poll/meeting_id:-> meeting/ FIELD 1r: => option/poll_id:-> poll/ -SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id +*** 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id + Field with reference temporarely needs also to-attribute SQL nt:1rR => option/vote_ids:-> vote/option_id FIELD 1Gr:,, => option/content_object_id:-> motion/,user/,poll_candidate_list/ FIELD 1rR: => option/meeting_id:-> meeting/ @@ -2621,22 +2576,38 @@ SQL 1t:1GrR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_ SQL nt:1GrR => mediafile/projection_ids:-> projection/content_object_id SQL nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids FIELD 1GrR:, => mediafile/owner_id:-> meeting/,organization/ -SQL 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id -SQL 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id -SQL 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id -SQL 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id -SQL 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id -SQL 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id -SQL 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id -SQL 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id -SQL 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id -SQL 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id -SQL 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id -SQL 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id -SQL 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id -SQL 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id -SQL 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id -SQL 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id +*** 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id + Field with reference temporarely needs also to-attribute SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id @@ -2668,8 +2639,10 @@ SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id FIELD 1rR: => projector_message/meeting_id:-> meeting/ SQL nt:1GrR => projector_countdown/projection_ids:-> projection/content_object_id -SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id -SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id +*** 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id + Field with reference temporarely needs also to-attribute +*** 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id + Field with reference temporarely needs also to-attribute FIELD 1rR: => projector_countdown/meeting_id:-> meeting/ SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id @@ -2683,4 +2656,4 @@ FIELD 1rR: => chat_message/meeting_id:-> meeting/ */ -/* Missing attribute handling for constant, sql, reference, on_delete, equal_fields, unique, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, sql, on_delete, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index af2e56bb..d79f5dab 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -917,14 +917,16 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: reference = bool(field.get("reference")) # general rules of inconsistent field descriptions on field level - if field.get("sql") == "": - error = "sql attribute may not be empty" + if reference and not to: # temporaray rule to keep all to-attributes + error = "Field with reference temporarely needs also to-attribute\n" + elif field.get("sql") == "": + error = "sql attribute may not be empty\n" elif required and sql: - error = "Field with attribute sql cannot be required" + error = "Field with attribute sql cannot be required\n" elif not (to or reference): - error = "Relation field must have `to` or `reference` attribut set" + error = "Relation field must have `to` or `reference` attribut set\n" elif field["type"] == "generic-relation-list" and required: - error = "generic-relation-list cannot be required: not implemented" + error = "generic-relation-list cannot be required: not implemented\n" if field["type"] == "relation": result = "1" @@ -978,16 +980,13 @@ def check_relation_definitions( foreign_collectionfields = [] for foreign_field in foreign_fields: foreign_c, tmp_error = Helper.get_cardinality(foreign_field) - if own_c == "1tR" and foreign_c == "1r": - raise Exception( - f"{own_field.table}.{own_field.column}:Change this in moduls.yml to 1rR:1t or 1t:1rR, the opposite side of a required can't build a sql with reference, but with to-attribut." - ) foreigns_c.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) if error: state = FIELD_SQL_ERROR_ENUM.ERROR + primary = False else: for i, foreign_field in enumerate(foreign_fields): if i == 0: From 6438beb1e61b0e70db5d96026d23c9ba074f8bab Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 3 Apr 2024 16:46:49 +0200 Subject: [PATCH 036/142] Apply PR65 readd 'to' relation fields --- dev/sql/schema_relational.sql | 260 +++++++++++++++++++--------------- models.yml | 61 +++++++- 2 files changed, 206 insertions(+), 115 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 2d3f3464..f7f62ea3 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'a0ab7cba150b1eeae6c16bb3e658496a' +-- MODELS_YML_CHECKSUM = 'dbc5b5491743339d9d9f0768c791ba80' -- Type definitions DO $$ BEGIN @@ -457,6 +457,7 @@ CREATE TABLE IF NOT EXISTS organizationT ( saml_metadata_idp text, saml_metadata_sp text, saml_private_key text, + theme_id integer NOT NULL, users_email_sender varchar(256) DEFAULT 'OpenSlides', users_email_replyto varchar(256), users_email_subject varchar(256) DEFAULT 'OpenSlides access data', @@ -506,7 +507,8 @@ CREATE TABLE IF NOT EXISTS userT ( last_login timestamptz, organization_management_level enum_user_organization_management_level, committee_ids integer[], - meeting_ids integer[] + meeting_ids integer[], + organization_id integer NOT NULL ); @@ -534,7 +536,8 @@ CREATE TABLE IF NOT EXISTS meeting_userT ( CREATE TABLE IF NOT EXISTS organization_tagT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, - color integer CHECK (color >= 0 and color <= 16777215) NOT NULL + color integer CHECK (color >= 0 and color <= 16777215) NOT NULL, + organization_id integer NOT NULL ); @@ -588,7 +591,8 @@ CREATE TABLE IF NOT EXISTS themeT ( headbar integer CHECK (headbar >= 0 and headbar <= 16777215), yes integer CHECK (yes >= 0 and yes <= 16777215), no integer CHECK (no >= 0 and no <= 16777215), - abstain integer CHECK (abstain >= 0 and abstain <= 16777215) + abstain integer CHECK (abstain >= 0 and abstain <= 16777215), + organization_id integer NOT NULL ); @@ -599,8 +603,10 @@ CREATE TABLE IF NOT EXISTS committeeT ( name varchar(256) NOT NULL, description text, external_id varchar(256), + default_meeting_id integer, user_ids integer[], - forwarding_user_id integer + forwarding_user_id integer, + organization_id integer NOT NULL ); @@ -764,10 +770,29 @@ This email was generated automatically.', poll_default_onehundred_percent_base enum_meeting_poll_default_onehundred_percent_base DEFAULT 'YNA', poll_default_backend enum_meeting_poll_default_backend DEFAULT 'fast', poll_couple_countdown boolean DEFAULT True, + logo_projector_main_id integer, + logo_projector_header_id integer, + logo_web_header_id integer, + logo_pdf_header_l_id integer, + logo_pdf_header_r_id integer, + logo_pdf_footer_l_id integer, + logo_pdf_footer_r_id integer, + logo_pdf_ballot_paper_id integer, + font_regular_id integer, + font_italic_id integer, + font_bold_id integer, + font_bold_italic_id integer, + font_monospace_id integer, + font_chyron_speaker_name_id integer, + font_projector_h1_id integer, + font_projector_h2_id integer, committee_id integer NOT NULL, user_ids integer[], reference_projector_id integer NOT NULL, - default_group_id integer NOT NULL + list_of_speakers_countdown_id integer, + poll_countdown_id integer, + default_group_id integer NOT NULL, + admin_group_id integer ); @@ -969,6 +994,7 @@ CREATE TABLE IF NOT EXISTS motionT ( sort_parent_id integer, origin_id integer, origin_meeting_id integer, + identical_motion_ids integer[], state_id integer NOT NULL, recommendation_id integer, category_id integer, @@ -981,13 +1007,8 @@ CREATE TABLE IF NOT EXISTS motionT ( comment on column motionT.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; comment on column motionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motionT.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can't be generated.'; -/* - Fields without SQL definition for table motion - - identical_motion_ids type:dummy[] Unknown Type - -*/ CREATE TABLE IF NOT EXISTS motion_submitterT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, @@ -1176,6 +1197,7 @@ CREATE TABLE IF NOT EXISTS pollT ( content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), + global_option_id integer, meeting_id integer NOT NULL ); @@ -1595,10 +1617,14 @@ CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_groupT ( -- View definitions CREATE OR REPLACE VIEW organization AS SELECT *, +(select array_agg(c.id) from committeeT c) as committee_ids, (select array_agg(m.id) from meetingT m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meetingT m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, (select array_agg(m.id) from meetingT m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids +(select array_agg(ot.id) from organization_tagT ot) as organization_tag_ids, +(select array_agg(t.id) from themeT t) as theme_ids, +(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(u.id) from userT u) as user_ids FROM organizationT o; @@ -1635,6 +1661,11 @@ CREATE OR REPLACE VIEW organization_tag AS SELECT *, FROM organization_tagT o; +CREATE OR REPLACE VIEW theme AS SELECT *, +(select o.id from organizationT o where o.theme_id = t.id) as theme_for_organization_id +FROM themeT t; + + CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(m.id) from meetingT m where m.committee_id = c.id) as meeting_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_userT n where n.committee_id = c.id) as manager_ids, @@ -1687,6 +1718,7 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(c.id) from chat_groupT c where c.meeting_id = m.id) as chat_group_ids, (select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, (select array_agg(s.id) from structure_levelT s where s.meeting_id = m.id) as structure_level_ids, +(select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, (select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids, @@ -1716,6 +1748,7 @@ FROM structure_levelT s; CREATE OR REPLACE VIEW group_ AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_userT n where n.group_id = g.id) as meeting_user_ids, (select m.id from meetingT m where m.default_group_id = g.id) as default_group_for_meeting_id, +(select m.id from meetingT m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_access_group_ids, (select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_inherited_access_group_ids, (select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as read_comment_section_ids, @@ -1844,6 +1877,7 @@ FROM pollT p; CREATE OR REPLACE VIEW option AS SELECT *, +(select p.id from pollT p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, (select array_agg(v.id) from voteT v where v.option_id = o.id) as vote_ids FROM optionT o; @@ -1871,7 +1905,23 @@ CREATE OR REPLACE VIEW mediafile AS SELECT *, (select array_agg(m1.id) from mediafileT m1 where m1.parent_id = m.id) as child_ids, (select l.id from list_of_speakersT l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, (select array_agg(p.id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids +(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids, +(select m1.id from meetingT m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, +(select m1.id from meetingT m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, +(select m1.id from meetingT m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id FROM mediafileT m; @@ -1889,7 +1939,9 @@ FROM projector_messageT p; CREATE OR REPLACE VIEW projector_countdown AS SELECT *, -(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids +(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select m.id from meetingT m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, +(select m.id from meetingT m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdownT p; @@ -1900,11 +1952,21 @@ CREATE OR REPLACE VIEW chat_group AS SELECT *, FROM chat_groupT c; -- Alter table relations +ALTER TABLE organizationT ADD FOREIGN KEY(theme_id) REFERENCES themeT(id) INITIALLY DEFERRED; + +ALTER TABLE userT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); + ALTER TABLE meeting_userT ADD FOREIGN KEY(user_id) REFERENCES userT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_userT(id); +ALTER TABLE organization_tagT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); + +ALTER TABLE themeT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id) INITIALLY DEFERRED; + +ALTER TABLE committeeT ADD FOREIGN KEY(default_meeting_id) REFERENCES meetingT(id); ALTER TABLE committeeT ADD FOREIGN KEY(forwarding_user_id) REFERENCES userT(id); +ALTER TABLE committeeT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); ALTER TABLE meetingT ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organizationT(id); ALTER TABLE meetingT ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organizationT(id); @@ -1912,9 +1974,28 @@ ALTER TABLE meetingT ADD FOREIGN KEY(template_for_organization_id) REFERENCES or ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_regular_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_italic_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafileT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafileT(id); ALTER TABLE meetingT ADD FOREIGN KEY(committee_id) REFERENCES committeeT(id); ALTER TABLE meetingT ADD FOREIGN KEY(reference_projector_id) REFERENCES projectorT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdownT(id); +ALTER TABLE meetingT ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdownT(id); ALTER TABLE meetingT ADD FOREIGN KEY(default_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; +ALTER TABLE meetingT ADD FOREIGN KEY(admin_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; ALTER TABLE structure_levelT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); @@ -2007,6 +2088,7 @@ ALTER TABLE motion_statute_paragraphT ADD FOREIGN KEY(meeting_id) REFERENCES mee ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); +ALTER TABLE pollT ADD FOREIGN KEY(global_option_id) REFERENCES optionT(id); ALTER TABLE pollT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE optionT ADD FOREIGN KEY(poll_id) REFERENCES pollT(id); @@ -2212,20 +2294,15 @@ Model.Field -> Model.Field */ /* -*** nrs: => organization/committee_ids:-> committee/ - Field with reference temporarely needs also to-attribute +SQL nrs: => organization/committee_ids:-> committee/ SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id -*** nr: => organization/organization_tag_ids:-> organization_tag/ - Field with reference temporarely needs also to-attribute -*** 1rR: => organization/theme_id:-> theme/ - Field with reference temporarely needs also to-attribute -*** nr: => organization/theme_ids:-> theme/ - Field with reference temporarely needs also to-attribute +SQL nr: => organization/organization_tag_ids:-> organization_tag/ +FIELD 1rR: => organization/theme_id:-> theme/ +SQL nr: => organization/theme_ids:-> theme/ SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id -*** nr: => organization/user_ids:-> user/ - Field with reference temporarely needs also to-attribute +SQL nr: => organization/user_ids:-> user/ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids @@ -2236,6 +2313,7 @@ SQL nt:1Gr => user/option_ids:-> option/content_object_id SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id +FIELD 1rR: => user/organization_id:-> organization/ FIELD 1rR: => meeting_user/user_id:-> user/ FIELD 1rR: => meeting_user/meeting_id:-> meeting/ @@ -2253,18 +2331,19 @@ SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids +FIELD 1rR: => organization_tag/organization_id:-> organization/ -*** 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id - Field with reference temporarely needs also to-attribute +SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id +FIELD 1rR: => theme/organization_id:-> organization/ SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id -*** 1r: => committee/default_meeting_id:-> meeting/ - Field with reference temporarely needs also to-attribute +FIELD 1r: => committee/default_meeting_id:-> meeting/ SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids FIELD 1r: => committee/forwarding_user_id:-> user/ SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids +FIELD 1rR: => committee/organization_id:-> organization/ FIELD 1r: => meeting/is_active_in_organization_id:-> organization/ FIELD 1r: => meeting/is_archived_in_organization_id:-> organization/ @@ -2314,48 +2393,29 @@ SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1rR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1rR => meeting/chat_message_ids:-> chat_message/meeting_id SQL nt:1rR => meeting/structure_level_ids:-> structure_level/meeting_id -*** 1r: => meeting/logo_projector_main_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_projector_header_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_web_header_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_regular_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_italic_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_bold_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_bold_italic_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_monospace_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_projector_h1_id:-> mediafile/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/font_projector_h2_id:-> mediafile/ - Field with reference temporarely needs also to-attribute +FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ +FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ +FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ +FIELD 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ +FIELD 1r: => meeting/font_regular_id:-> mediafile/ +FIELD 1r: => meeting/font_italic_id:-> mediafile/ +FIELD 1r: => meeting/font_bold_id:-> mediafile/ +FIELD 1r: => meeting/font_bold_italic_id:-> mediafile/ +FIELD 1r: => meeting/font_monospace_id:-> mediafile/ +FIELD 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ +FIELD 1r: => meeting/font_projector_h1_id:-> mediafile/ +FIELD 1r: => meeting/font_projector_h2_id:-> mediafile/ FIELD 1rR: => meeting/committee_id:-> committee/ -*** 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id - Field with reference temporarely needs also to-attribute +SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids FIELD 1rR: => meeting/reference_projector_id:-> projector/ -*** 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ - Field with reference temporarely needs also to-attribute -*** 1r: => meeting/poll_countdown_id:-> projector_countdown/ - Field with reference temporarely needs also to-attribute +FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ +FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ SQL nt:1GrR => meeting/projection_ids:-> projection/content_object_id SQL ntR:1r => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id SQL ntR:1r => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id @@ -2372,8 +2432,7 @@ SQL ntR:1r => meeting/default_projector_assignment_poll_ids:-> projector/used_as SQL ntR:1r => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id FIELD 1rR: => meeting/default_group_id:-> group/ -*** 1r: => meeting/admin_group_id:-> group/ - Field with reference temporarely needs also to-attribute +FIELD 1r: => meeting/admin_group_id:-> group/ SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id @@ -2381,8 +2440,7 @@ FIELD 1rR: => structure_level/meeting_id:-> meeting/ SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id -*** 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id - Field with reference temporarely needs also to-attribute +SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids @@ -2528,16 +2586,14 @@ FIELD 1rR: => motion_statute_paragraph/meeting_id:-> meeting/ FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ SQL nt:1r => poll/option_ids:-> option/poll_id -*** 1r: => poll/global_option_id:-> option/ - Field with reference temporarely needs also to-attribute +FIELD 1r: => poll/global_option_id:-> option/ SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id FIELD 1rR: => poll/meeting_id:-> meeting/ FIELD 1r: => option/poll_id:-> poll/ -*** 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id - Field with reference temporarely needs also to-attribute +SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id SQL nt:1rR => option/vote_ids:-> vote/option_id FIELD 1Gr:,, => option/content_object_id:-> motion/,user/,poll_candidate_list/ FIELD 1rR: => option/meeting_id:-> meeting/ @@ -2576,38 +2632,22 @@ SQL 1t:1GrR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_ SQL nt:1GrR => mediafile/projection_ids:-> projection/content_object_id SQL nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids FIELD 1GrR:, => mediafile/owner_id:-> meeting/,organization/ -*** 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id - Field with reference temporarely needs also to-attribute +SQL 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id +SQL 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id +SQL 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id +SQL 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id +SQL 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id +SQL 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id +SQL 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id +SQL 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id +SQL 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id +SQL 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id +SQL 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id +SQL 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id +SQL 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id +SQL 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id +SQL 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id +SQL 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id @@ -2639,10 +2679,8 @@ SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id FIELD 1rR: => projector_message/meeting_id:-> meeting/ SQL nt:1GrR => projector_countdown/projection_ids:-> projection/content_object_id -*** 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id - Field with reference temporarely needs also to-attribute -*** 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id - Field with reference temporarely needs also to-attribute +SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id +SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id FIELD 1rR: => projector_countdown/meeting_id:-> meeting/ SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id diff --git a/models.yml b/models.yml index b3910d20..d9178535 100644 --- a/models.yml +++ b/models.yml @@ -167,6 +167,7 @@ organization: committee_ids: type: relation-list + to: committee/organization_id restriction_mode: B sql: (select array_agg(c.id) from committeeT c) as committee_ids reference: committee @@ -185,15 +186,18 @@ organization: organization_tag_ids: type: relation-list restriction_mode: B + to: organization_tag/organization_id reference: organization_tag(id) theme_id: type: relation required: true restriction_mode: A + to: theme/theme_for_organization_id reference: theme(id) theme_ids: type: relation-list restriction_mode: A + to: theme/organization_id reference: theme(id) mediafile_ids: type: relation-list @@ -203,6 +207,7 @@ organization: user_ids: type: relation-list restriction_mode: C + to: user/organization_id reference: user(id) users_email_sender: type: string @@ -382,6 +387,13 @@ user: description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true restriction_mode: E + organization_id: + type: relation + reference: organization + to: organization/user_ids + required: True + restriction_mode: F + constant: true meeting_user: id: @@ -505,6 +517,12 @@ organization_tag: - meeting field: organization_tag_ids restriction_mode: A + organization_id: + type: relation + reference: organization + to: organization/organization_tag_ids + restriction_mode: A + required: true theme: id: @@ -661,6 +679,12 @@ theme: restriction_mode: A to: organization/theme_id type: relation + organization_id: + type: relation + reference: organization + to: organization/theme_ids + restriction_mode: A + required: true committee: id: @@ -685,6 +709,7 @@ committee: restriction_mode: A default_meeting_id: type: relation + to: meeting/default_meeting_for_committee_id reference: meeting(id) restriction_mode: A user_ids: @@ -724,6 +749,13 @@ committee: type: relation-list to: organization_tag/tagged_ids restriction_mode: A + organization_id: + type: relation + reference: organization + to: organization/committee_ids + required: true + restriction_mode: A + constant: true meeting: id: @@ -1681,66 +1713,82 @@ meeting: # Logos and Fonts logo_projector_main_id: type: relation + to: mediafile/used_as_logo_projector_main_in_meeting_id reference: mediafile(id) restriction_mode: B logo_projector_header_id: type: relation + to: mediafile/used_as_logo_projector_header_in_meeting_id reference: mediafile(id) restriction_mode: B logo_web_header_id: type: relation + to: mediafile/used_as_logo_web_header_in_meeting_id reference: mediafile(id) restriction_mode: B logo_pdf_header_l_id: type: relation + to: mediafile/used_as_logo_pdf_header_l_in_meeting_id reference: mediafile(id) restriction_mode: B logo_pdf_header_r_id: type: relation + to: mediafile/used_as_logo_pdf_header_r_in_meeting_id reference: mediafile(id) restriction_mode: B logo_pdf_footer_l_id: type: relation + to: mediafile/used_as_logo_pdf_header_l_in_meeting_id reference: mediafile(id) restriction_mode: B logo_pdf_footer_r_id: type: relation + to: mediafile/used_as_logo_pdf_header_l_in_meeting_id reference: mediafile(id) restriction_mode: B logo_pdf_ballot_paper_id: type: relation + to: mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id reference: mediafile(id) restriction_mode: B font_regular_id: type: relation + to: mediafile/used_as_font_regular_in_meeting_id reference: mediafile(id) restriction_mode: B font_italic_id: type: relation + to: mediafile/used_as_font_italic_in_meeting_id reference: mediafile(id) restriction_mode: B font_bold_id: type: relation + to: mediafile/used_as_font_bold_in_meeting_id reference: mediafile(id) restriction_mode: B font_bold_italic_id: type: relation + to: mediafile/used_as_font_bold_italic_in_meeting_id reference: mediafile(id) restriction_mode: B font_monospace_id: type: relation + to: mediafile/used_as_font_monospace_in_meeting_id reference: mediafile(id) restriction_mode: B font_chyron_speaker_name_id: type: relation + to: mediafile/used_as_font_chyron_speaker_name_in_meeting_id reference: mediafile(id) restriction_mode: B font_projector_h1_id: type: relation + to: mediafile/used_as_font_projector_h1_in_meeting_id reference: mediafile(id) restriction_mode: B font_projector_h2_id: type: relation + to: mediafile/used_as_font_projector_h2_in_meeting_id reference: mediafile(id) restriction_mode: B # Other relations @@ -1776,10 +1824,12 @@ meeting: restriction_mode: B list_of_speakers_countdown_id: type: relation + to: projector_countdown/used_as_list_of_speakers_countdown_meeting_id reference: projector_countdown(id) restriction_mode: B poll_countdown_id: type: relation + to: projector_countdown/used_as_poll_countdown_meeting_id reference: projector_countdown(id) restriction_mode: B projection_ids: @@ -1865,6 +1915,7 @@ meeting: restriction_mode: B admin_group_id: type: relation + to: group/admin_group_for_meeting_id reference: group(id) restriction_mode: B @@ -2610,9 +2661,10 @@ motion: to: motion/all_origin_ids restriction_mode: A identical_motion_ids: - type: dummy[] - # type relation-list - description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number.Changed from relation-list to number[], because can't still be generated + type: number[] + # type: relation-list + # to: motion/identical_motion_ids + description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can't be generated. restriction_mode: C state_id: type: relation @@ -3485,6 +3537,7 @@ poll: restriction_mode: A global_option_id: type: relation + to: option/used_as_global_option_in_poll_id reference: option(id) on_delete: CASCADE equal_fields: meeting_id @@ -4086,7 +4139,7 @@ projector: used_as_default_projector_for_current_los_in_meeting_id: type: relation reference: meeting - to: meeting/default_projector_current_list_of_speakers_ids + to: meeting/default_projector_current_los_ids restriction_mode: A used_as_default_projector_for_motion_in_meeting_id: type: relation From 647fd81f7b84a300336b521a796a019e38b20243 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 3 Apr 2024 17:51:52 +0200 Subject: [PATCH 037/142] escape ' in comment --- models.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.yml b/models.yml index d9178535..4d2352b5 100644 --- a/models.yml +++ b/models.yml @@ -2664,7 +2664,7 @@ motion: type: number[] # type: relation-list # to: motion/identical_motion_ids - description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can't be generated. + description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated. restriction_mode: C state_id: type: relation From 583ef7ac493c38f2eaefa348e4dce0e5551be71d Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 3 Apr 2024 19:06:40 +0200 Subject: [PATCH 038/142] define fields organization_id automatically as number with value 1 --- dev/sql/schema_relational.sql | 23 ++++++----------------- dev/src/generate_sql_schema.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index f7f62ea3..495841df 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'dbc5b5491743339d9d9f0768c791ba80' +-- MODELS_YML_CHECKSUM = '7dcd1c2adf5ed87d5664417ed5edda2c' -- Type definitions DO $$ BEGIN @@ -508,7 +508,7 @@ CREATE TABLE IF NOT EXISTS userT ( organization_management_level enum_user_organization_management_level, committee_ids integer[], meeting_ids integer[], - organization_id integer NOT NULL + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -537,7 +537,7 @@ CREATE TABLE IF NOT EXISTS organization_tagT ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, color integer CHECK (color >= 0 and color <= 16777215) NOT NULL, - organization_id integer NOT NULL + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -592,7 +592,7 @@ CREATE TABLE IF NOT EXISTS themeT ( yes integer CHECK (yes >= 0 and yes <= 16777215), no integer CHECK (no >= 0 and no <= 16777215), abstain integer CHECK (abstain >= 0 and abstain <= 16777215), - organization_id integer NOT NULL + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -606,7 +606,7 @@ CREATE TABLE IF NOT EXISTS committeeT ( default_meeting_id integer, user_ids integer[], forwarding_user_id integer, - organization_id integer NOT NULL + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -1007,7 +1007,7 @@ CREATE TABLE IF NOT EXISTS motionT ( comment on column motionT.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; comment on column motionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column motionT.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can't be generated.'; +comment on column motionT.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated.'; CREATE TABLE IF NOT EXISTS motion_submitterT ( @@ -1954,19 +1954,12 @@ FROM chat_groupT c; -- Alter table relations ALTER TABLE organizationT ADD FOREIGN KEY(theme_id) REFERENCES themeT(id) INITIALLY DEFERRED; -ALTER TABLE userT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); - ALTER TABLE meeting_userT ADD FOREIGN KEY(user_id) REFERENCES userT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); ALTER TABLE meeting_userT ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_userT(id); -ALTER TABLE organization_tagT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); - -ALTER TABLE themeT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id) INITIALLY DEFERRED; - ALTER TABLE committeeT ADD FOREIGN KEY(default_meeting_id) REFERENCES meetingT(id); ALTER TABLE committeeT ADD FOREIGN KEY(forwarding_user_id) REFERENCES userT(id); -ALTER TABLE committeeT ADD FOREIGN KEY(organization_id) REFERENCES organizationT(id); ALTER TABLE meetingT ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organizationT(id); ALTER TABLE meetingT ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organizationT(id); @@ -2313,7 +2306,6 @@ SQL nt:1Gr => user/option_ids:-> option/content_object_id SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id -FIELD 1rR: => user/organization_id:-> organization/ FIELD 1rR: => meeting_user/user_id:-> user/ FIELD 1rR: => meeting_user/meeting_id:-> meeting/ @@ -2331,10 +2323,8 @@ SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids -FIELD 1rR: => organization_tag/organization_id:-> organization/ SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id -FIELD 1rR: => theme/organization_id:-> organization/ SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r: => committee/default_meeting_id:-> meeting/ @@ -2343,7 +2333,6 @@ SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids FIELD 1r: => committee/forwarding_user_id:-> user/ SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids -FIELD 1rR: => committee/organization_id:-> organization/ FIELD 1r: => meeting/is_active_in_organization_id:-> organization/ FIELD 1r: => meeting/is_archived_in_organization_id:-> organization/ diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index d79f5dab..a9e405ce 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -182,6 +182,10 @@ def get_method( ) if fname == "id": type_ = "primary_key" + elif ( + fname == "organization_id" + ): # temporary, just to fill the 4 organization_id-fields automatically with 1 + type_ = "organization_id" else: type_ = fdata.get("type", "") if type_ in FIELD_TYPES: @@ -233,6 +237,16 @@ def get_schema_primary_key( text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) return text + @classmethod + def get_schema_organization_id( + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str + ) -> SchemaZoneTexts: + text: SchemaZoneTexts + subst, text = Helper.get_initials(table_name, fname, type_, fdata) + subst["primary_key"] = " GENERATED ALWAYS AS (1) STORED" + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text + @classmethod def get_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str @@ -1221,6 +1235,10 @@ def get_definitions_from_foreign_list( "pg_type": "integer", "method": GenerateCodeBlocks.get_schema_primary_key, }, + "organization_id": { + "pg_type": "integer", + "method": GenerateCodeBlocks.get_schema_organization_id, + }, } From 861f75d420da3fae953a603c3cd45805b4d6da48 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 4 Apr 2024 15:28:49 +0200 Subject: [PATCH 039/142] remove unnecessary elements from decision list --- dev/src/generate_sql_schema.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index a9e405ce..813a67a2 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1037,22 +1037,15 @@ def generate_field_or_sql_decision( tuple[str, str], tuple[FIELD_SQL_ERROR_ENUM | None, bool | str | None] ] = { ("1Gr", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1Gr", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1Gr", "1tR"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1GrR", "1t"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1GrR", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("1GrR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("1r", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("1rR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("1t", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), ("1t", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), ("1t", "1rR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1r", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1r", "ntR"): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("1tR", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), ("1tR", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), ("1rR", "1t"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1rR", "nt"): (FIELD_SQL_ERROR_ENUM.FIELD, False), ("nGt", "nt"): (FIELD_SQL_ERROR_ENUM.SQL, True), ("nr", ""): (FIELD_SQL_ERROR_ENUM.SQL, True), ("nt", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), From 65f17dabd37b69d8227dae9ab8ed4a5bfb725bfd Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 4 Apr 2024 16:48:37 +0200 Subject: [PATCH 040/142] readd .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 34a682ff..5e504384 100644 --- a/.gitignore +++ b/.gitignore @@ -32,5 +32,3 @@ secrets/ # code-workspaces *.code-workspace - -.gitignore From f9c7f789676df30bdd60d55aa9f86bf6460bcb91 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 4 Apr 2024 17:14:55 +0200 Subject: [PATCH 041/142] fix annotations from code review --- dev/src/generate_sql_schema.py | 81 +++++++++++----------- models.yml | 118 ++++++++++++++++++++------------- 2 files changed, 114 insertions(+), 85 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 813a67a2..f976d3ee 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -9,7 +9,8 @@ from textwrap import dedent from typing import Any, TypedDict, cast -from helper_get_names import HelperGetNames, InternalHelper, TableFieldType +from helper_get_names import (KEYSEPARATOR, HelperGetNames, InternalHelper, + TableFieldType) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() @@ -43,7 +44,7 @@ class SQL_Delete_Update_Options(str, Enum): NO_ACTION = "NO ACTION" -class FIELD_SQL_ERROR_ENUM(Enum): +class FieldSqlErrorType(Enum): FIELD = 1 SQL = 2 ERROR = 3 @@ -262,7 +263,7 @@ def get_relation_type( own_table_field, [foreign_table_field] ) - if state == FIELD_SQL_ERROR_ENUM.FIELD: + if state == FieldSqlErrorType.FIELD: text.update(cls.get_schema_simple_types(table_name, fname, fdata, "number")) initially_deferred = fdata.get( "deferred" @@ -278,7 +279,7 @@ def get_relation_type( initially_deferred, ) ) - elif state == FIELD_SQL_ERROR_ENUM.SQL: + elif state == FieldSqlErrorType.SQL: if sql := fdata.get("sql", ""): text["view"] = sql + ",\n" elif foreign_table_field.field_def["type"] == "generic-relation": @@ -331,7 +332,7 @@ def get_relation_list_type( own_table_field, [foreign_table_field] ) - if state != FIELD_SQL_ERROR_ENUM.ERROR: + if state != FieldSqlErrorType.ERROR: if primary: if foreign_table_field.field_def.get("type") == "relation-list": nm_table_name, value = Helper.get_nm_table_for_n_m_relation_lists( @@ -466,7 +467,7 @@ def get_generic_relation_type( own_table_field, foreign_table_fields ) - if state == FIELD_SQL_ERROR_ENUM.FIELD: + if state == FieldSqlErrorType.FIELD: text.update( cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"]) ) @@ -515,7 +516,7 @@ def get_generic_relation_list_type( own_table_field, foreign_table_fields ) - if state == FIELD_SQL_ERROR_ENUM.SQL and primary: + if state == FieldSqlErrorType.SQL and primary: # create gm-intermediate table gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( own_table_field, foreign_table_fields @@ -971,7 +972,7 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: @staticmethod def check_relation_definitions( own_field: TableFieldType, foreign_fields: list[TableFieldType] - ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str, str]: + ) -> tuple[FieldSqlErrorType, bool, str, str]: """ Decides for the own-field, - whether it is a field, a sql-expression or is there an error @@ -982,7 +983,7 @@ def check_relation_definitions( case of an error an error text Returns: - - field, sql, error => enum FIELD_SQL_ERROR_ENUM + - field, sql, error => enum FieldSqlErrorType - primary field (only relevant for list fields) - complete relational text with FIELD, SQL or *** in front - error line if error else empty string @@ -999,7 +1000,7 @@ def check_relation_definitions( foreign_collectionfields.append(foreign_field.collectionfield) if error: - state = FIELD_SQL_ERROR_ENUM.ERROR + state = FieldSqlErrorType.ERROR primary = False else: for i, foreign_field in enumerate(foreign_fields): @@ -1014,50 +1015,50 @@ def check_relation_definitions( if not error and (statex != state or primaryx != primary): error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" if error: - state = FIELD_SQL_ERROR_ENUM.ERROR + state = FieldSqlErrorType.ERROR break - state_text = "***" if state == FIELD_SQL_ERROR_ENUM.ERROR else state.name + state_text = "***" if state == FieldSqlErrorType.ERROR else state.name text = f"{state_text} {own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" - if state == FIELD_SQL_ERROR_ENUM.ERROR: + if state == FieldSqlErrorType.ERROR: text += f" {error}" return state, primary, text, error @staticmethod def generate_field_or_sql_decision( own: TableFieldType, own_c: str, foreign: TableFieldType, foreign_c: str - ) -> tuple[FIELD_SQL_ERROR_ENUM, bool, str]: + ) -> tuple[FieldSqlErrorType, bool, str]: """ Returns: - - field, sql, error for own => enum FIELD_SQL_ERROR_ENUM + - field, sql, error for own => enum FieldSqlErrorType - primary field for own: (only relevant for list fields) - error line if error else empty string """ decision_list: dict[ - tuple[str, str], tuple[FIELD_SQL_ERROR_ENUM | None, bool | str | None] + tuple[str, str], tuple[FieldSqlErrorType | None, bool | str | None] ] = { - ("1Gr", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1GrR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1r", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1rR", ""): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("1t", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1t", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1t", "1rR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1tR", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1tR", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("1rR", "1t"): (FIELD_SQL_ERROR_ENUM.FIELD, False), - ("nGt", "nt"): (FIELD_SQL_ERROR_ENUM.SQL, True), - ("nr", ""): (FIELD_SQL_ERROR_ENUM.SQL, True), - ("nt", "1Gr"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("nt", "1GrR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("nt", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("nt", "1rR"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("nt", "nGt"): (FIELD_SQL_ERROR_ENUM.SQL, False), - ("nt", "nt"): (FIELD_SQL_ERROR_ENUM.SQL, "primary_decide_alphabetical"), - ("ntR", "1r"): (FIELD_SQL_ERROR_ENUM.SQL, False), + ("1Gr", ""): (FieldSqlErrorType.FIELD, False), + ("1GrR", ""): (FieldSqlErrorType.FIELD, False), + ("1r", ""): (FieldSqlErrorType.FIELD, False), + ("1rR", ""): (FieldSqlErrorType.FIELD, False), + ("1t", "1GrR"): (FieldSqlErrorType.SQL, False), + ("1t", "1r"): (FieldSqlErrorType.SQL, False), + ("1t", "1rR"): (FieldSqlErrorType.SQL, False), + ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), + ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), + ("1rR", "1t"): (FieldSqlErrorType.FIELD, False), + ("nGt", "nt"): (FieldSqlErrorType.SQL, True), + ("nr", ""): (FieldSqlErrorType.SQL, True), + ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), + ("nt", "1GrR"): (FieldSqlErrorType.SQL, False), + ("nt", "1r"): (FieldSqlErrorType.SQL, False), + ("nt", "1rR"): (FieldSqlErrorType.SQL, False), + ("nt", "nGt"): (FieldSqlErrorType.SQL, False), + ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), + ("ntR", "1r"): (FieldSqlErrorType.SQL, False), } - state: FIELD_SQL_ERROR_ENUM | str | None + state: FieldSqlErrorType | str | None primary: bool | str | None error = "" @@ -1066,16 +1067,16 @@ def generate_field_or_sql_decision( ) if state is None: error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" - state = FIELD_SQL_ERROR_ENUM.ERROR + state = FieldSqlErrorType.ERROR elif primary == "primary_decide_alphabetical": if own.collectionfield == foreign.collectionfield: error = f"Field {own.collectionfield} identical with foreign.collectionfield. SQL_decice_alphabetical uncedidable!\n" - state = FIELD_SQL_ERROR_ENUM.ERROR + state = FieldSqlErrorType.ERROR primary = ( foreign.collectionfield == "-" or own.collectionfield < foreign.collectionfield ) - return cast(FIELD_SQL_ERROR_ENUM, state), cast(bool, primary), error + return cast(FieldSqlErrorType, state), cast(bool, primary), error @staticmethod def get_generic_combined_fields( @@ -1122,7 +1123,7 @@ def get_foreign_table_from_to_or_reference( re_groups = result.groups() return re_groups[0] elif to: - return to.split("/")[0] + return to.split(KEYSEPARATOR)[0] else: raise Exception("Relation field without reference or to") diff --git a/models.yml b/models.yml index 4d2352b5..56df5149 100644 --- a/models.yml +++ b/models.yml @@ -4,7 +4,7 @@ # - HTMLStrict: A string with HTML content. # - HTMLPermissive: A string with HTML content (with video tags). # - float: Numbers that are expected to be non-integer. Formatted as in rfc7159. -# - decimal(X): Decimal values represented as a string with X decimal places. +# - decimal(): Decimal values represented as a string with X decimal places. # At the moment we support only X == 6. # - timestamp: Datetime as a unix timestamp. Why a number? This enables queries # in the DB. And we do not need more precision than 1 second. @@ -18,15 +18,12 @@ # in case of n:m-relations with an intermediary table. # - Non-generic relations: The simple syntax for such a field # `to: /`. This is a reference to a collection-field, the "other" side of a relation. -# In the relational DB this will be a field in a view filled by an automatically generated sql. -# During the development of the relational db one relation can have a `to` and a `reference` property. -# In this case the `to` value is only used for historical reason, onky the `reference` property is used -# for the schema generation. +# In the relational DB this will be a field in a view filled by generated sql. # If necessary the sql can be written manually, see `sql`. -# `reference: `. The field `id` will be assumed. This will create a foreign key constraint. Therefore a `reference` is # the indicator for a real field in the table and you cannot use it for relation types with a `list` suffix. # For some time there may be also a `to`-property for the relation, but for schema generation -# the `reference` takes the precedence over the `to`. +# the `reference` takes precedence over the `to`. # `deferred: true`: this field get the "INITIALLY DEFERRED" on it's foreign key definition # - Generic relations: The difference to non-generic relations is that you have a # list of possible fields, so `to` can either hold multiple collections (if the @@ -42,10 +39,41 @@ # - motion/option_ids # - user/option_ids # - poll_candidate_list/option_id -# The `reference`, only for generic-relation, but not generic-relation-list will be a list of references: +# For a generic-relation (not for generic-relation-list, see above), the attribute `reference` is a list of references: # reference: -# - agenda_item(id) -# - assignment(id) +# - agenda_item +# - assignment +# List of allowed and implemented relations +# Symbols: +# 1 = relation +# 1G = generic-relation +# n = relation-list +# nG = generic-relation-list +# r = reference, will result in a FIELD +# to = to, collection with field get's an automatic sql in a view, if not together with `reference` +# R = required +# The first tuple symbols the relation from-to, the 2nd FIELD or SQL and primary or not, only valis for lists +# The original of the following list is used in source code as `decision_list` +# ("1Gr", ""): (FieldSqlErrorType.FIELD, False), +# ("1GrR", ""): (FieldSqlErrorType.FIELD, False), +# ("1r", ""): (FieldSqlErrorType.FIELD, False), +# ("1rR", ""): (FieldSqlErrorType.FIELD, False), +# ("1t", "1GrR"): (FieldSqlErrorType.SQL, False), +# ("1t", "1r"): (FieldSqlErrorType.SQL, False), +# ("1t", "1rR"): (FieldSqlErrorType.SQL, False), +# ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), +# ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), +# ("1rR", "1t"): (FieldSqlErrorType.FIELD, False), +# ("nGt", "nt"): (FieldSqlErrorType.SQL, True), +# ("nr", ""): (FieldSqlErrorType.SQL, True), +# ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), +# ("nt", "1GrR"): (FieldSqlErrorType.SQL, False), +# ("nt", "1r"): (FieldSqlErrorType.SQL, False), +# ("nt", "1rR"): (FieldSqlErrorType.SQL, False), +# ("nt", "nGt"): (FieldSqlErrorType.SQL, False), +# ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), +# ("ntR", "1r"): (FieldSqlErrorType.SQL, False), +# # - on_delete: This fields determines what should happen with the foreign model if # this model gets deleted. Possible values are: # - SET_NULL (default): delete the id from the foreign key @@ -56,8 +84,8 @@ # - You can add JSON Schema properties to the fields like `enum`, `description`, # `items`, `maxLength` and `minimum` # Additional properties: -# - The property `sql` can be filled manually with an sql-statement, which fills this field. This -# field can't be filled with an own vaue and is always defined in a view. You can use it on all +# - The optional property `sql` may contain an SQL statement which is used to calculate the value of this field. This +# field does not exist with its own value in a table, but is always defined in a view. It can be used on all # field types, not only relations. # - The property `read_only` describes a field that can not be changed by an action. # - The property `default` describes the default value that is used for new objects. @@ -187,18 +215,18 @@ organization: type: relation-list restriction_mode: B to: organization_tag/organization_id - reference: organization_tag(id) + reference: organization_tag theme_id: type: relation required: true restriction_mode: A to: theme/theme_for_organization_id - reference: theme(id) + reference: theme theme_ids: type: relation-list restriction_mode: A to: theme/organization_id - reference: theme(id) + reference: theme mediafile_ids: type: relation-list to: mediafile/owner_id @@ -208,7 +236,7 @@ organization: type: relation-list restriction_mode: C to: user/organization_id - reference: user(id) + reference: user users_email_sender: type: string default: OpenSlides @@ -425,7 +453,7 @@ meeting_user: constant: true meeting_id: type: relation - reference: meeting(id) + reference: meeting to: meeting/meeting_user_ids required: true restriction_mode: A @@ -470,7 +498,7 @@ meeting_user: restriction_mode: A vote_delegated_to_id: type: relation - reference: meeting_user(id) + reference: meeting_user to: meeting_user/vote_delegations_from_ids equal_fields: meeting_id restriction_mode: A @@ -710,7 +738,7 @@ committee: default_meeting_id: type: relation to: meeting/default_meeting_for_committee_id - reference: meeting(id) + reference: meeting restriction_mode: A user_ids: type: number[] @@ -1714,82 +1742,82 @@ meeting: logo_projector_main_id: type: relation to: mediafile/used_as_logo_projector_main_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_projector_header_id: type: relation to: mediafile/used_as_logo_projector_header_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_web_header_id: type: relation to: mediafile/used_as_logo_web_header_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_pdf_header_l_id: type: relation to: mediafile/used_as_logo_pdf_header_l_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_pdf_header_r_id: type: relation to: mediafile/used_as_logo_pdf_header_r_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_pdf_footer_l_id: type: relation to: mediafile/used_as_logo_pdf_header_l_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_pdf_footer_r_id: type: relation to: mediafile/used_as_logo_pdf_header_l_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B logo_pdf_ballot_paper_id: type: relation to: mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_regular_id: type: relation to: mediafile/used_as_font_regular_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_italic_id: type: relation to: mediafile/used_as_font_italic_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_bold_id: type: relation to: mediafile/used_as_font_bold_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_bold_italic_id: type: relation to: mediafile/used_as_font_bold_italic_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_monospace_id: type: relation to: mediafile/used_as_font_monospace_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_chyron_speaker_name_id: type: relation to: mediafile/used_as_font_chyron_speaker_name_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_projector_h1_id: type: relation to: mediafile/used_as_font_projector_h1_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B font_projector_h2_id: type: relation to: mediafile/used_as_font_projector_h2_in_meeting_id - reference: mediafile(id) + reference: mediafile restriction_mode: B # Other relations committee_id: @@ -1825,12 +1853,12 @@ meeting: list_of_speakers_countdown_id: type: relation to: projector_countdown/used_as_list_of_speakers_countdown_meeting_id - reference: projector_countdown(id) + reference: projector_countdown restriction_mode: B poll_countdown_id: type: relation to: projector_countdown/used_as_poll_countdown_meeting_id - reference: projector_countdown(id) + reference: projector_countdown restriction_mode: B projection_ids: type: relation-list @@ -1909,14 +1937,14 @@ meeting: required: true default_group_id: type: relation - reference: group(id) + reference: group to: group/default_group_for_meeting_id required: true restriction_mode: B admin_group_id: type: relation to: group/admin_group_for_meeting_id - reference: group(id) + reference: group restriction_mode: B structure_level: @@ -3538,7 +3566,7 @@ poll: global_option_id: type: relation to: option/used_as_global_option_in_poll_id - reference: option(id) + reference: option on_delete: CASCADE equal_fields: meeting_id restriction_mode: A @@ -3657,7 +3685,7 @@ vote: constant: true user_id: type: relation - reference: user(id) + reference: user to: user/vote_ids restriction_mode: A delegated_user_id: @@ -3782,7 +3810,7 @@ assignment_candidate: constant: true meeting_user_id: type: relation - reference: meeting_user(id) + reference: meeting_user to: meeting_user/assignment_candidate_ids restriction_mode: A constant: true @@ -3936,8 +3964,8 @@ mediafile: owner_id: type: generic-relation reference: - - meeting(id) - - organization(id) + - meeting + - organization to: - meeting/mediafile_ids - organization/mediafile_ids @@ -4406,7 +4434,7 @@ chat_message: restriction_mode: A meeting_user_id: type: relation - reference: meeting_user(id) + reference: meeting_user to: meeting_user/chat_message_ids restriction_mode: A required: true From 275ff6899d5e8cdb481de6855a12c2b42a6e4b3d Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 4 Apr 2024 17:38:29 +0200 Subject: [PATCH 042/142] fix format eror --- dev/src/generate_sql_schema.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index f976d3ee..fdf6ee5d 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -9,8 +9,12 @@ from textwrap import dedent from typing import Any, TypedDict, cast -from helper_get_names import (KEYSEPARATOR, HelperGetNames, InternalHelper, - TableFieldType) +from helper_get_names import ( + KEYSEPARATOR, + HelperGetNames, + InternalHelper, + TableFieldType, +) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() From 8581da502ee07f13206bee1ae4044eaf31ae2544 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 5 Apr 2024 11:34:06 +0200 Subject: [PATCH 043/142] change table names: use appendix "_t" --- dev/sql/schema_relational.sql | 1266 ++++++++++++++++----------------- dev/src/helper_get_names.py | 4 +- 2 files changed, 635 insertions(+), 635 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 495841df..641e24fa 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '7dcd1c2adf5ed87d5664417ed5edda2c' +-- MODELS_YML_CHECKSUM = '439bb5c7ec340920a013cfb408687436' -- Type definitions DO $$ BEGIN @@ -437,7 +437,7 @@ END$$; -- Table definitions -CREATE TABLE IF NOT EXISTS organizationT ( +CREATE TABLE IF NOT EXISTS organization_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256), description text, @@ -476,8 +476,8 @@ This email was generated automatically.', -comment on column organizationT.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; -comment on column organizationT.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; +comment on column organization_t.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; +comment on column organization_t.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; /* Fields without SQL definition for table organization @@ -486,7 +486,7 @@ comment on column organizationT.limit_of_users is 'Maximum of active users for t */ -CREATE TABLE IF NOT EXISTS userT ( +CREATE TABLE IF NOT EXISTS user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, username varchar(256) NOT NULL, saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), @@ -513,13 +513,13 @@ CREATE TABLE IF NOT EXISTS userT ( -comment on column userT.saml_id is 'unique-key from IdP for SAML login'; -comment on column userT.organization_management_level is 'Hierarchical permission level for the whole organization.'; -comment on column userT.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; -comment on column userT.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; +comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; +comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; +comment on column user_t.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column user_t.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; -CREATE TABLE IF NOT EXISTS meeting_userT ( +CREATE TABLE IF NOT EXISTS meeting_user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, comment text, number varchar(256), @@ -533,7 +533,7 @@ CREATE TABLE IF NOT EXISTS meeting_userT ( -CREATE TABLE IF NOT EXISTS organization_tagT ( +CREATE TABLE IF NOT EXISTS organization_tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, color integer CHECK (color >= 0 and color <= 16777215) NOT NULL, @@ -543,7 +543,7 @@ CREATE TABLE IF NOT EXISTS organization_tagT ( -CREATE TABLE IF NOT EXISTS themeT ( +CREATE TABLE IF NOT EXISTS theme_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, accent_100 integer CHECK (accent_100 >= 0 and accent_100 <= 16777215), @@ -598,7 +598,7 @@ CREATE TABLE IF NOT EXISTS themeT ( -CREATE TABLE IF NOT EXISTS committeeT ( +CREATE TABLE IF NOT EXISTS committee_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, description text, @@ -611,11 +611,11 @@ CREATE TABLE IF NOT EXISTS committeeT ( -comment on column committeeT.external_id is 'unique'; -comment on column committeeT.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; +comment on column committee_t.external_id is 'unique'; +comment on column committee_t.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; -CREATE TABLE IF NOT EXISTS meetingT ( +CREATE TABLE IF NOT EXISTS meeting_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', @@ -797,15 +797,15 @@ This email was generated automatically.', -comment on column meetingT.external_id is 'unique in committee'; -comment on column meetingT.is_active_in_organization_id is 'Backrelation and boolean flag at once'; -comment on column meetingT.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; -comment on column meetingT.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; -comment on column meetingT.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meetingT.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; +comment on column meeting_t.external_id is 'unique in committee'; +comment on column meeting_t.is_active_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; +comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; +comment on column meeting_t.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; -CREATE TABLE IF NOT EXISTS structure_levelT ( +CREATE TABLE IF NOT EXISTS structure_level_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, color integer CHECK (color >= 0 and color <= 16777215), @@ -816,7 +816,7 @@ CREATE TABLE IF NOT EXISTS structure_levelT ( -CREATE TABLE IF NOT EXISTS groupT ( +CREATE TABLE IF NOT EXISTS group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), name varchar(256) NOT NULL, @@ -831,10 +831,10 @@ CREATE TABLE IF NOT EXISTS groupT ( -comment on column groupT.external_id is 'unique in meeting'; +comment on column group_t.external_id is 'unique in meeting'; -CREATE TABLE IF NOT EXISTS personal_noteT ( +CREATE TABLE IF NOT EXISTS personal_note_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, note text, star boolean, @@ -848,7 +848,7 @@ CREATE TABLE IF NOT EXISTS personal_noteT ( -CREATE TABLE IF NOT EXISTS tagT ( +CREATE TABLE IF NOT EXISTS tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, meeting_id integer NOT NULL @@ -857,7 +857,7 @@ CREATE TABLE IF NOT EXISTS tagT ( -CREATE TABLE IF NOT EXISTS agenda_itemT ( +CREATE TABLE IF NOT EXISTS agenda_item_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, item_number varchar(256), comment varchar(256), @@ -881,13 +881,13 @@ CREATE TABLE IF NOT EXISTS agenda_itemT ( -comment on column agenda_itemT.duration is 'Given in seconds'; -comment on column agenda_itemT.is_internal is 'Calculated by the server'; -comment on column agenda_itemT.is_hidden is 'Calculated by the server'; -comment on column agenda_itemT.level is 'Calculated by the server'; +comment on column agenda_item_t.duration is 'Given in seconds'; +comment on column agenda_item_t.is_internal is 'Calculated by the server'; +comment on column agenda_item_t.is_hidden is 'Calculated by the server'; +comment on column agenda_item_t.level is 'Calculated by the server'; -CREATE TABLE IF NOT EXISTS list_of_speakersT ( +CREATE TABLE IF NOT EXISTS list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, closed boolean DEFAULT False, sequential_number integer NOT NULL, @@ -903,10 +903,10 @@ CREATE TABLE IF NOT EXISTS list_of_speakersT ( -comment on column list_of_speakersT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column list_of_speakers_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS structure_level_list_of_speakersT ( +CREATE TABLE IF NOT EXISTS structure_level_list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, structure_level_id integer NOT NULL, list_of_speakers_id integer NOT NULL, @@ -919,13 +919,13 @@ CREATE TABLE IF NOT EXISTS structure_level_list_of_speakersT ( -comment on column structure_level_list_of_speakersT.initial_time is 'The initial time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakersT.additional_time is 'The summed added time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakersT.remaining_time is 'The currently remaining time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakersT.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; +comment on column structure_level_list_of_speakers_t.initial_time is 'The initial time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.additional_time is 'The summed added time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.remaining_time is 'The currently remaining time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; -CREATE TABLE IF NOT EXISTS point_of_order_categoryT ( +CREATE TABLE IF NOT EXISTS point_of_order_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, text varchar(256) NOT NULL, rank integer NOT NULL, @@ -935,7 +935,7 @@ CREATE TABLE IF NOT EXISTS point_of_order_categoryT ( -CREATE TABLE IF NOT EXISTS speakerT ( +CREATE TABLE IF NOT EXISTS speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, begin_time timestamptz, end_time timestamptz, @@ -956,7 +956,7 @@ CREATE TABLE IF NOT EXISTS speakerT ( -CREATE TABLE IF NOT EXISTS topicT ( +CREATE TABLE IF NOT EXISTS topic_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, text text, @@ -966,10 +966,10 @@ CREATE TABLE IF NOT EXISTS topicT ( -comment on column topicT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column topic_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motionT ( +CREATE TABLE IF NOT EXISTS motion_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, number varchar(256), number_value integer, @@ -1005,12 +1005,12 @@ CREATE TABLE IF NOT EXISTS motionT ( -comment on column motionT.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; -comment on column motionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column motionT.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated.'; +comment on column motion_t.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; +comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_t.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated.'; -CREATE TABLE IF NOT EXISTS motion_submitterT ( +CREATE TABLE IF NOT EXISTS motion_submitter_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -1021,7 +1021,7 @@ CREATE TABLE IF NOT EXISTS motion_submitterT ( -CREATE TABLE IF NOT EXISTS motion_editorT ( +CREATE TABLE IF NOT EXISTS motion_editor_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -1032,7 +1032,7 @@ CREATE TABLE IF NOT EXISTS motion_editorT ( -CREATE TABLE IF NOT EXISTS motion_working_group_speakerT ( +CREATE TABLE IF NOT EXISTS motion_working_group_speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -1043,7 +1043,7 @@ CREATE TABLE IF NOT EXISTS motion_working_group_speakerT ( -CREATE TABLE IF NOT EXISTS motion_commentT ( +CREATE TABLE IF NOT EXISTS motion_comment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, comment text, motion_id integer NOT NULL, @@ -1054,7 +1054,7 @@ CREATE TABLE IF NOT EXISTS motion_commentT ( -CREATE TABLE IF NOT EXISTS motion_comment_sectionT ( +CREATE TABLE IF NOT EXISTS motion_comment_section_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer DEFAULT 10000, @@ -1065,10 +1065,10 @@ CREATE TABLE IF NOT EXISTS motion_comment_sectionT ( -comment on column motion_comment_sectionT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_comment_section_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_categoryT ( +CREATE TABLE IF NOT EXISTS motion_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, prefix varchar(256), @@ -1081,11 +1081,11 @@ CREATE TABLE IF NOT EXISTS motion_categoryT ( -comment on column motion_categoryT.level is 'Calculated field.'; -comment on column motion_categoryT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_category_t.level is 'Calculated field.'; +comment on column motion_category_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_blockT ( +CREATE TABLE IF NOT EXISTS motion_block_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, internal boolean, @@ -1095,10 +1095,10 @@ CREATE TABLE IF NOT EXISTS motion_blockT ( -comment on column motion_blockT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_block_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_change_recommendationT ( +CREATE TABLE IF NOT EXISTS motion_change_recommendation_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, rejected boolean DEFAULT False, internal boolean DEFAULT False, @@ -1115,7 +1115,7 @@ CREATE TABLE IF NOT EXISTS motion_change_recommendationT ( -CREATE TABLE IF NOT EXISTS motion_stateT ( +CREATE TABLE IF NOT EXISTS motion_state_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer NOT NULL, @@ -1140,7 +1140,7 @@ CREATE TABLE IF NOT EXISTS motion_stateT ( -CREATE TABLE IF NOT EXISTS motion_workflowT ( +CREATE TABLE IF NOT EXISTS motion_workflow_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, sequential_number integer NOT NULL, @@ -1150,10 +1150,10 @@ CREATE TABLE IF NOT EXISTS motion_workflowT ( -comment on column motion_workflowT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_workflow_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_statute_paragraphT ( +CREATE TABLE IF NOT EXISTS motion_statute_paragraph_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, text text, @@ -1164,10 +1164,10 @@ CREATE TABLE IF NOT EXISTS motion_statute_paragraphT ( -comment on column motion_statute_paragraphT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_statute_paragraph_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS pollT ( +CREATE TABLE IF NOT EXISTS poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, description text, title varchar(256) NOT NULL, @@ -1203,11 +1203,11 @@ CREATE TABLE IF NOT EXISTS pollT ( -comment on column pollT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column pollT.crypt_key is 'base64 public key to cryptographic votes.'; -comment on column pollT.crypt_signature is 'base64 signature of cryptographic_key.'; -comment on column pollT.votes_raw is 'original form of decrypted votes.'; -comment on column pollT.votes_signature is 'base64 signature of votes_raw field.'; +comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column poll_t.crypt_key is 'base64 public key to cryptographic votes.'; +comment on column poll_t.crypt_signature is 'base64 signature of cryptographic_key.'; +comment on column poll_t.votes_raw is 'original form of decrypted votes.'; +comment on column poll_t.votes_signature is 'base64 signature of votes_raw field.'; /* Fields without SQL definition for table poll @@ -1216,7 +1216,7 @@ comment on column pollT.votes_signature is 'base64 signature of votes_raw field. */ -CREATE TABLE IF NOT EXISTS optionT ( +CREATE TABLE IF NOT EXISTS option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer DEFAULT 10000, text text, @@ -1235,7 +1235,7 @@ CREATE TABLE IF NOT EXISTS optionT ( -CREATE TABLE IF NOT EXISTS voteT ( +CREATE TABLE IF NOT EXISTS vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight decimal(6), value varchar(256), @@ -1249,7 +1249,7 @@ CREATE TABLE IF NOT EXISTS voteT ( -CREATE TABLE IF NOT EXISTS assignmentT ( +CREATE TABLE IF NOT EXISTS assignment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, description text, @@ -1263,10 +1263,10 @@ CREATE TABLE IF NOT EXISTS assignmentT ( -comment on column assignmentT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column assignment_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS assignment_candidateT ( +CREATE TABLE IF NOT EXISTS assignment_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer DEFAULT 10000, assignment_id integer NOT NULL, @@ -1277,7 +1277,7 @@ CREATE TABLE IF NOT EXISTS assignment_candidateT ( -CREATE TABLE IF NOT EXISTS poll_candidate_listT ( +CREATE TABLE IF NOT EXISTS poll_candidate_list_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, meeting_id integer NOT NULL ); @@ -1285,7 +1285,7 @@ CREATE TABLE IF NOT EXISTS poll_candidate_listT ( -CREATE TABLE IF NOT EXISTS poll_candidateT ( +CREATE TABLE IF NOT EXISTS poll_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, poll_candidate_list_id integer NOT NULL, user_id integer, @@ -1296,7 +1296,7 @@ CREATE TABLE IF NOT EXISTS poll_candidateT ( -CREATE TABLE IF NOT EXISTS mediafileT ( +CREATE TABLE IF NOT EXISTS mediafile_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256), is_directory boolean, @@ -1316,13 +1316,13 @@ CREATE TABLE IF NOT EXISTS mediafileT ( -comment on column mediafileT.title is 'Title and parent_id must be unique.'; -comment on column mediafileT.filesize is 'In bytes, not the human readable format anymore.'; -comment on column mediafileT.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; -comment on column mediafileT.is_public is 'Calculated field. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; +comment on column mediafile_t.title is 'Title and parent_id must be unique.'; +comment on column mediafile_t.filesize is 'In bytes, not the human readable format anymore.'; +comment on column mediafile_t.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; +comment on column mediafile_t.is_public is 'Calculated field. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; -CREATE TABLE IF NOT EXISTS projectorT ( +CREATE TABLE IF NOT EXISTS projector_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256), is_internal boolean DEFAULT False, @@ -1362,10 +1362,10 @@ CREATE TABLE IF NOT EXISTS projectorT ( -comment on column projectorT.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column projector_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS projectionT ( +CREATE TABLE IF NOT EXISTS projection_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, options jsonb, stable boolean DEFAULT False, @@ -1399,7 +1399,7 @@ CREATE TABLE IF NOT EXISTS projectionT ( */ -CREATE TABLE IF NOT EXISTS projector_messageT ( +CREATE TABLE IF NOT EXISTS projector_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, message text, meeting_id integer NOT NULL @@ -1408,7 +1408,7 @@ CREATE TABLE IF NOT EXISTS projector_messageT ( -CREATE TABLE IF NOT EXISTS projector_countdownT ( +CREATE TABLE IF NOT EXISTS projector_countdown_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, description varchar(256) DEFAULT '', @@ -1421,7 +1421,7 @@ CREATE TABLE IF NOT EXISTS projector_countdownT ( -CREATE TABLE IF NOT EXISTS chat_groupT ( +CREATE TABLE IF NOT EXISTS chat_group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer DEFAULT 10000, @@ -1431,7 +1431,7 @@ CREATE TABLE IF NOT EXISTS chat_groupT ( -CREATE TABLE IF NOT EXISTS chat_messageT ( +CREATE TABLE IF NOT EXISTS chat_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, content text NOT NULL, created timestamptz NOT NULL, @@ -1443,7 +1443,7 @@ CREATE TABLE IF NOT EXISTS chat_messageT ( -CREATE TABLE IF NOT EXISTS action_workerT ( +CREATE TABLE IF NOT EXISTS action_worker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, state enum_action_worker_state NOT NULL, @@ -1455,7 +1455,7 @@ CREATE TABLE IF NOT EXISTS action_workerT ( -CREATE TABLE IF NOT EXISTS import_previewT ( +CREATE TABLE IF NOT EXISTS import_preview_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name enum_import_preview_name NOT NULL, state enum_import_preview_state NOT NULL, @@ -1469,21 +1469,21 @@ CREATE TABLE IF NOT EXISTS import_previewT ( -- Intermediate table definitions -CREATE TABLE IF NOT EXISTS nm_meeting_user_supported_motion_ids_motionT ( - meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), - motion_id integer NOT NULL REFERENCES motionT (id), +CREATE TABLE IF NOT EXISTS nm_meeting_user_supported_motion_ids_motion_t ( + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), + motion_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (meeting_user_id, motion_id) ); -CREATE TABLE IF NOT EXISTS nm_meeting_user_structure_level_ids_structure_levelT ( - meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), - structure_level_id integer NOT NULL REFERENCES structure_levelT (id), +CREATE TABLE IF NOT EXISTS nm_meeting_user_structure_level_ids_structure_level_t ( + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), + structure_level_id integer NOT NULL REFERENCES structure_level_t (id), PRIMARY KEY (meeting_user_id, structure_level_id) ); -CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_idsT ( +CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - organization_tag_id integer NOT NULL REFERENCES organization_tagT(id), + organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id), tagged_id varchar(100) NOT NULL, tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committeeT(id), tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meetingT(id), @@ -1491,63 +1491,63 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_idsT ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_userT ( - committee_id integer NOT NULL REFERENCES committeeT (id), - user_id integer NOT NULL REFERENCES userT (id), +CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_user_t ( + committee_id integer NOT NULL REFERENCES committee_t (id), + user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_forward_to_committee_ids_committeeT ( - forward_to_committee_id integer NOT NULL REFERENCES committeeT (id), - receive_forwardings_from_committee_id integer NOT NULL REFERENCES committeeT (id), +CREATE TABLE IF NOT EXISTS nm_committee_forward_to_committee_ids_committee_t ( + forward_to_committee_id integer NOT NULL REFERENCES committee_t (id), + receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id), PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); -CREATE TABLE IF NOT EXISTS nm_meeting_present_user_ids_userT ( - meeting_id integer NOT NULL REFERENCES meetingT (id), - user_id integer NOT NULL REFERENCES userT (id), +CREATE TABLE IF NOT EXISTS nm_meeting_present_user_ids_user_t ( + meeting_id integer NOT NULL REFERENCES meeting_t (id), + user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (meeting_id, user_id) ); -CREATE TABLE IF NOT EXISTS nm_group_meeting_user_ids_meeting_userT ( - group_id integer NOT NULL REFERENCES groupT (id), - meeting_user_id integer NOT NULL REFERENCES meeting_userT (id), +CREATE TABLE IF NOT EXISTS nm_group_meeting_user_ids_meeting_user_t ( + group_id integer NOT NULL REFERENCES group_t (id), + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), PRIMARY KEY (group_id, meeting_user_id) ); -CREATE TABLE IF NOT EXISTS nm_group_mediafile_access_group_ids_mediafileT ( - group_id integer NOT NULL REFERENCES groupT (id), - mediafile_id integer NOT NULL REFERENCES mediafileT (id), +CREATE TABLE IF NOT EXISTS nm_group_mediafile_access_group_ids_mediafile_t ( + group_id integer NOT NULL REFERENCES group_t (id), + mediafile_id integer NOT NULL REFERENCES mediafile_t (id), PRIMARY KEY (group_id, mediafile_id) ); -CREATE TABLE IF NOT EXISTS nm_group_mediafile_inherited_access_group_ids_mediafileT ( - group_id integer NOT NULL REFERENCES groupT (id), - mediafile_id integer NOT NULL REFERENCES mediafileT (id), +CREATE TABLE IF NOT EXISTS nm_group_mediafile_inherited_access_group_ids_mediafile_t ( + group_id integer NOT NULL REFERENCES group_t (id), + mediafile_id integer NOT NULL REFERENCES mediafile_t (id), PRIMARY KEY (group_id, mediafile_id) ); -CREATE TABLE IF NOT EXISTS nm_group_read_comment_section_ids_motion_comment_sectionT ( - group_id integer NOT NULL REFERENCES groupT (id), - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_sectionT (id), +CREATE TABLE IF NOT EXISTS nm_group_read_comment_section_ids_motion_comment_section_t ( + group_id integer NOT NULL REFERENCES group_t (id), + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE IF NOT EXISTS nm_group_write_comment_section_ids_motion_comment_sectionT ( - group_id integer NOT NULL REFERENCES groupT (id), - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_sectionT (id), +CREATE TABLE IF NOT EXISTS nm_group_write_comment_section_ids_motion_comment_section_t ( + group_id integer NOT NULL REFERENCES group_t (id), + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE IF NOT EXISTS nm_group_poll_ids_pollT ( - group_id integer NOT NULL REFERENCES groupT (id), - poll_id integer NOT NULL REFERENCES pollT (id), +CREATE TABLE IF NOT EXISTS nm_group_poll_ids_poll_t ( + group_id integer NOT NULL REFERENCES group_t (id), + poll_id integer NOT NULL REFERENCES poll_t (id), PRIMARY KEY (group_id, poll_id) ); -CREATE TABLE IF NOT EXISTS gm_tag_tagged_idsT ( +CREATE TABLE IF NOT EXISTS gm_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - tag_id integer NOT NULL REFERENCES tagT(id), + tag_id integer NOT NULL REFERENCES tag_t(id), tagged_id varchar(100) NOT NULL, tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_itemT(id), tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), @@ -1556,45 +1556,45 @@ CREATE TABLE IF NOT EXISTS gm_tag_tagged_idsT ( CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); -CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motionT ( - all_derived_motion_id integer NOT NULL REFERENCES motionT (id), - all_origin_id integer NOT NULL REFERENCES motionT (id), +CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motion_t ( + all_derived_motion_id integer NOT NULL REFERENCES motion_t (id), + all_origin_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (all_derived_motion_id, all_origin_id) ); -CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_idsT ( +CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motionT(id), + motion_id integer NOT NULL REFERENCES motion_t(id), state_extension_reference_id varchar(100) NOT NULL, state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); -CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_idsT ( +CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motionT(id), + motion_id integer NOT NULL REFERENCES motion_t(id), recommendation_extension_reference_id varchar(100) NOT NULL, recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); -CREATE TABLE IF NOT EXISTS nm_motion_state_next_state_ids_motion_stateT ( - next_state_id integer NOT NULL REFERENCES motion_stateT (id), - previous_state_id integer NOT NULL REFERENCES motion_stateT (id), +CREATE TABLE IF NOT EXISTS nm_motion_state_next_state_ids_motion_state_t ( + next_state_id integer NOT NULL REFERENCES motion_state_t (id), + previous_state_id integer NOT NULL REFERENCES motion_state_t (id), PRIMARY KEY (next_state_id, previous_state_id) ); -CREATE TABLE IF NOT EXISTS nm_poll_voted_ids_userT ( - poll_id integer NOT NULL REFERENCES pollT (id), - user_id integer NOT NULL REFERENCES userT (id), +CREATE TABLE IF NOT EXISTS nm_poll_voted_ids_user_t ( + poll_id integer NOT NULL REFERENCES poll_t (id), + user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_idsT ( +CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - mediafile_id integer NOT NULL REFERENCES mediafileT(id), + mediafile_id integer NOT NULL REFERENCES mediafile_t(id), attachment_id varchar(100) NOT NULL, attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topicT(id), @@ -1603,667 +1603,667 @@ CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_idsT ( CONSTRAINT unique_$mediafile_id_$attachment_id UNIQUE (mediafile_id, attachment_id) ); -CREATE TABLE IF NOT EXISTS nm_chat_group_read_group_ids_groupT ( - chat_group_id integer NOT NULL REFERENCES chat_groupT (id), - group_id integer NOT NULL REFERENCES groupT (id), +CREATE TABLE IF NOT EXISTS nm_chat_group_read_group_ids_group_t ( + chat_group_id integer NOT NULL REFERENCES chat_group_t (id), + group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); -CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_groupT ( - chat_group_id integer NOT NULL REFERENCES chat_groupT (id), - group_id integer NOT NULL REFERENCES groupT (id), +CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_group_t ( + chat_group_id integer NOT NULL REFERENCES chat_group_t (id), + group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); -- View definitions CREATE OR REPLACE VIEW organization AS SELECT *, (select array_agg(c.id) from committeeT c) as committee_ids, -(select array_agg(m.id) from meetingT m where m.is_active_in_organization_id = o.id) as active_meeting_ids, -(select array_agg(m.id) from meetingT m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, -(select array_agg(m.id) from meetingT m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(ot.id) from organization_tagT ot) as organization_tag_ids, -(select array_agg(t.id) from themeT t) as theme_ids, -(select array_agg(m.id) from mediafileT m where m.owner_id_organization_id = o.id) as mediafile_ids, -(select array_agg(u.id) from userT u) as user_ids -FROM organizationT o; +(select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, +(select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, +(select array_agg(m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, +(select array_agg(ot.id) from organization_tag_t ot) as organization_tag_ids, +(select array_agg(t.id) from theme_t t) as theme_ids, +(select array_agg(m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(u.id) from user_t u) as user_ids +FROM organization_t o; CREATE OR REPLACE VIEW user_ AS SELECT *, -(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_userT n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(n.committee_id) from nm_committee_manager_ids_userT n where n.user_id = u.id) as committee_management_ids, -(select array_agg(c.id) from committeeT c where c.forwarding_user_id = u.id) as forwarding_committee_ids, -(select array_agg(m.id) from meeting_userT m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id) from nm_poll_voted_ids_userT n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(o.id) from optionT o where o.content_object_id_user_id = u.id) as option_ids, -(select array_agg(v.id) from voteT v where v.user_id = u.id) as vote_ids, -(select array_agg(v.id) from voteT v where v.delegated_user_id = u.id) as delegated_vote_ids, -(select array_agg(p.id) from poll_candidateT p where p.user_id = u.id) as poll_candidate_ids -FROM userT u; +(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, +(select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, +(select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, +(select array_agg(n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(v.id) from vote_t v where v.user_id = u.id) as vote_ids, +(select array_agg(v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids +FROM user_t u; CREATE OR REPLACE VIEW meeting_user AS SELECT *, -(select array_agg(p.id) from personal_noteT p where p.meeting_user_id = m.id) as personal_note_ids, -(select array_agg(s.id) from speakerT s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motionT n where n.meeting_user_id = m.id) as supported_motion_ids, -(select array_agg(me.id) from motion_editorT me where me.meeting_user_id = m.id) as motion_editor_ids, -(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, -(select array_agg(ms.id) from motion_submitterT ms where ms.meeting_user_id = m.id) as motion_submitter_ids, -(select array_agg(a.id) from assignment_candidateT a where a.meeting_user_id = m.id) as assignment_candidate_ids, -(select array_agg(mu.id) from meeting_userT mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, -(select array_agg(c.id) from chat_messageT c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_userT n where n.meeting_user_id = m.id) as group_ids, -(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_levelT n where n.meeting_user_id = m.id) as structure_level_ids -FROM meeting_userT m; +(select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, +(select array_agg(s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, +(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, +(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, +(select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, +(select array_agg(a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, +(select array_agg(mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, +(select array_agg(c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, +(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids +FROM meeting_user_t m; CREATE OR REPLACE VIEW organization_tag AS SELECT *, -(select array_agg(g.id) from gm_organization_tag_tagged_idsT g where g.organization_tag_id = o.id) as tagged_ids -FROM organization_tagT o; +(select array_agg(g.id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids +FROM organization_tag_t o; CREATE OR REPLACE VIEW theme AS SELECT *, -(select o.id from organizationT o where o.theme_id = t.id) as theme_for_organization_id -FROM themeT t; +(select o.id from organization_t o where o.theme_id = t.id) as theme_for_organization_id +FROM theme_t t; CREATE OR REPLACE VIEW committee AS SELECT *, -(select array_agg(m.id) from meetingT m where m.committee_id = c.id) as meeting_ids, -(select array_agg(n.user_id) from nm_committee_manager_ids_userT n where n.committee_id = c.id) as manager_ids, -(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, -(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committeeT n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_committee_id = c.id) as organization_tag_ids -FROM committeeT c; +(select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, +(select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, +(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, +(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids +FROM committee_t c; CREATE OR REPLACE VIEW meeting AS SELECT *, -(select array_agg(g.id) from groupT g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, -(select array_agg(p.id) from poll_candidate_listT p where p.meeting_id = m.id) as poll_candidate_list_ids, -(select array_agg(p.id) from poll_candidateT p where p.meeting_id = m.id) as poll_candidate_ids, -(select array_agg(mu.id) from meeting_userT mu where mu.meeting_id = m.id) as meeting_user_ids, -(select array_agg(g.id) from groupT g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, -(select array_agg(g.id) from groupT g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, -(select array_agg(g.id) from groupT g where g.used_as_topic_poll_default_id = m.id) as topic_poll_default_group_ids, -(select array_agg(p.id) from projectorT p where p.meeting_id = m.id) as projector_ids, -(select array_agg(p.id) from projectionT p where p.meeting_id = m.id) as all_projection_ids, -(select array_agg(p.id) from projector_messageT p where p.meeting_id = m.id) as projector_message_ids, -(select array_agg(p.id) from projector_countdownT p where p.meeting_id = m.id) as projector_countdown_ids, -(select array_agg(t.id) from tagT t where t.meeting_id = m.id) as tag_ids, -(select array_agg(a.id) from agenda_itemT a where a.meeting_id = m.id) as agenda_item_ids, -(select array_agg(l.id) from list_of_speakersT l where l.meeting_id = m.id) as list_of_speakers_ids, -(select array_agg(s.id) from structure_level_list_of_speakersT s where s.meeting_id = m.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.id) from point_of_order_categoryT p where p.meeting_id = m.id) as point_of_order_category_ids, -(select array_agg(s.id) from speakerT s where s.meeting_id = m.id) as speaker_ids, -(select array_agg(t.id) from topicT t where t.meeting_id = m.id) as topic_ids, -(select array_agg(g.id) from groupT g where g.meeting_id = m.id) as group_ids, -(select array_agg(m1.id) from mediafileT m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, -(select array_agg(m1.id) from motionT m1 where m1.meeting_id = m.id) as motion_ids, -(select array_agg(m1.id) from motionT m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, -(select array_agg(mc.id) from motion_comment_sectionT mc where mc.meeting_id = m.id) as motion_comment_section_ids, -(select array_agg(mc.id) from motion_categoryT mc where mc.meeting_id = m.id) as motion_category_ids, -(select array_agg(mb.id) from motion_blockT mb where mb.meeting_id = m.id) as motion_block_ids, -(select array_agg(mw.id) from motion_workflowT mw where mw.meeting_id = m.id) as motion_workflow_ids, -(select array_agg(ms.id) from motion_statute_paragraphT ms where ms.meeting_id = m.id) as motion_statute_paragraph_ids, -(select array_agg(mc.id) from motion_commentT mc where mc.meeting_id = m.id) as motion_comment_ids, -(select array_agg(ms.id) from motion_submitterT ms where ms.meeting_id = m.id) as motion_submitter_ids, -(select array_agg(me.id) from motion_editorT me where me.meeting_id = m.id) as motion_editor_ids, -(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, -(select array_agg(mc.id) from motion_change_recommendationT mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, -(select array_agg(ms.id) from motion_stateT ms where ms.meeting_id = m.id) as motion_state_ids, -(select array_agg(p.id) from pollT p where p.meeting_id = m.id) as poll_ids, -(select array_agg(o.id) from optionT o where o.meeting_id = m.id) as option_ids, -(select array_agg(v.id) from voteT v where v.meeting_id = m.id) as vote_ids, -(select array_agg(a.id) from assignmentT a where a.meeting_id = m.id) as assignment_ids, -(select array_agg(a.id) from assignment_candidateT a where a.meeting_id = m.id) as assignment_candidate_ids, -(select array_agg(p.id) from personal_noteT p where p.meeting_id = m.id) as personal_note_ids, -(select array_agg(c.id) from chat_groupT c where c.meeting_id = m.id) as chat_group_ids, -(select array_agg(c.id) from chat_messageT c where c.meeting_id = m.id) as chat_message_ids, -(select array_agg(s.id) from structure_levelT s where s.meeting_id = m.id) as structure_level_ids, -(select c.id from committeeT c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_idsT g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, -(select array_agg(n.user_id) from nm_meeting_present_user_ids_userT n where n.meeting_id = m.id) as present_user_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_meeting_id = m.id) as projection_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_list_of_speakers_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_assignment_in_meeting_id = m.id) as default_projector_assignment_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_mediafile_in_meeting_id = m.id) as default_projector_mediafile_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_message_in_meeting_id = m.id) as default_projector_message_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, -(select array_agg(p.id) from projectorT p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids -FROM meetingT m; +(select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, +(select array_agg(p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, +(select array_agg(p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, +(select array_agg(mu.id) from meeting_user_t mu where mu.meeting_id = m.id) as meeting_user_ids, +(select array_agg(g.id) from group_t g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, +(select array_agg(g.id) from group_t g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, +(select array_agg(g.id) from group_t g where g.used_as_topic_poll_default_id = m.id) as topic_poll_default_group_ids, +(select array_agg(p.id) from projector_t p where p.meeting_id = m.id) as projector_ids, +(select array_agg(p.id) from projection_t p where p.meeting_id = m.id) as all_projection_ids, +(select array_agg(p.id) from projector_message_t p where p.meeting_id = m.id) as projector_message_ids, +(select array_agg(p.id) from projector_countdown_t p where p.meeting_id = m.id) as projector_countdown_ids, +(select array_agg(t.id) from tag_t t where t.meeting_id = m.id) as tag_ids, +(select array_agg(a.id) from agenda_item_t a where a.meeting_id = m.id) as agenda_item_ids, +(select array_agg(l.id) from list_of_speakers_t l where l.meeting_id = m.id) as list_of_speakers_ids, +(select array_agg(s.id) from structure_level_list_of_speakers_t s where s.meeting_id = m.id) as structure_level_list_of_speakers_ids, +(select array_agg(p.id) from point_of_order_category_t p where p.meeting_id = m.id) as point_of_order_category_ids, +(select array_agg(s.id) from speaker_t s where s.meeting_id = m.id) as speaker_ids, +(select array_agg(t.id) from topic_t t where t.meeting_id = m.id) as topic_ids, +(select array_agg(g.id) from group_t g where g.meeting_id = m.id) as group_ids, +(select array_agg(m1.id) from mediafile_t m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, +(select array_agg(m1.id) from motion_t m1 where m1.meeting_id = m.id) as motion_ids, +(select array_agg(m1.id) from motion_t m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, +(select array_agg(mc.id) from motion_comment_section_t mc where mc.meeting_id = m.id) as motion_comment_section_ids, +(select array_agg(mc.id) from motion_category_t mc where mc.meeting_id = m.id) as motion_category_ids, +(select array_agg(mb.id) from motion_block_t mb where mb.meeting_id = m.id) as motion_block_ids, +(select array_agg(mw.id) from motion_workflow_t mw where mw.meeting_id = m.id) as motion_workflow_ids, +(select array_agg(ms.id) from motion_statute_paragraph_t ms where ms.meeting_id = m.id) as motion_statute_paragraph_ids, +(select array_agg(mc.id) from motion_comment_t mc where mc.meeting_id = m.id) as motion_comment_ids, +(select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_id = m.id) as motion_submitter_ids, +(select array_agg(me.id) from motion_editor_t me where me.meeting_id = m.id) as motion_editor_ids, +(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, +(select array_agg(mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, +(select array_agg(ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, +(select array_agg(p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, +(select array_agg(o.id) from option_t o where o.meeting_id = m.id) as option_ids, +(select array_agg(v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, +(select array_agg(a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, +(select array_agg(a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, +(select array_agg(p.id) from personal_note_t p where p.meeting_id = m.id) as personal_note_ids, +(select array_agg(c.id) from chat_group_t c where c.meeting_id = m.id) as chat_group_ids, +(select array_agg(c.id) from chat_message_t c where c.meeting_id = m.id) as chat_message_ids, +(select array_agg(s.id) from structure_level_t s where s.meeting_id = m.id) as structure_level_ids, +(select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(n.user_id) from nm_meeting_present_user_ids_user_t n where n.meeting_id = m.id) as present_user_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_list_of_speakers_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_assignment_in_meeting_id = m.id) as default_projector_assignment_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_mediafile_in_meeting_id = m.id) as default_projector_mediafile_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_message_in_meeting_id = m.id) as default_projector_message_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids +FROM meeting_t m; CREATE OR REPLACE VIEW structure_level AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_levelT n where n.structure_level_id = s.id) as meeting_user_ids, -(select array_agg(sl.id) from structure_level_list_of_speakersT sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids -FROM structure_levelT s; +(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids +FROM structure_level_t s; CREATE OR REPLACE VIEW group_ AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_userT n where n.group_id = g.id) as meeting_user_ids, -(select m.id from meetingT m where m.default_group_id = g.id) as default_group_for_meeting_id, -(select m.id from meetingT m where m.admin_group_id = g.id) as admin_group_for_meeting_id, -(select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_access_group_ids, -(select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.group_id = g.id) as mediafile_inherited_access_group_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as read_comment_section_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_sectionT n where n.group_id = g.id) as write_comment_section_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_groupT n where n.group_id = g.id) as read_chat_group_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_groupT n where n.group_id = g.id) as write_chat_group_ids, -(select array_agg(n.poll_id) from nm_group_poll_ids_pollT n where n.group_id = g.id) as poll_ids -FROM groupT g; +(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, +(select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, +(select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, +(select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_access_group_ids, +(select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_group_t n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids +FROM group_t g; CREATE OR REPLACE VIEW tag AS SELECT *, -(select array_agg(g.id) from gm_tag_tagged_idsT g where g.tag_id = t.id) as tagged_ids -FROM tagT t; +(select array_agg(g.id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids +FROM tag_t t; CREATE OR REPLACE VIEW agenda_item AS SELECT *, -(select array_agg(ai.id) from agenda_itemT ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_agenda_item_id = a.id) as tag_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_agenda_item_id = a.id) as projection_ids -FROM agenda_itemT a; +(select array_agg(ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, +(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids +FROM agenda_item_t a; CREATE OR REPLACE VIEW list_of_speakers AS SELECT *, -(select array_agg(s.id) from speakerT s where s.list_of_speakers_id = l.id) as speaker_ids, -(select array_agg(s.id) from structure_level_list_of_speakersT s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids -FROM list_of_speakersT l; +(select array_agg(s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, +(select array_agg(s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids +FROM list_of_speakers_t l; CREATE OR REPLACE VIEW structure_level_list_of_speakers AS SELECT *, -(select array_agg(s1.id) from speakerT s1 where s1.structure_level_list_of_speakers_id = s.id) as speaker_ids -FROM structure_level_list_of_speakersT s; +(select array_agg(s1.id) from speaker_t s1 where s1.structure_level_list_of_speakers_id = s.id) as speaker_ids +FROM structure_level_list_of_speakers_t s; CREATE OR REPLACE VIEW point_of_order_category AS SELECT *, -(select array_agg(s.id) from speakerT s where s.point_of_order_category_id = p.id) as speaker_ids -FROM point_of_order_categoryT p; +(select array_agg(s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids +FROM point_of_order_category_t p; CREATE OR REPLACE VIEW topic AS SELECT *, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_topic_id = t.id) as attachment_ids, -(select a.id from agenda_itemT a where a.content_object_id_topic_id = t.id) as agenda_item_id, -(select l.id from list_of_speakersT l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, -(select array_agg(p.id) from pollT p where p.content_object_id_topic_id = t.id) as poll_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_topic_id = t.id) as projection_ids -FROM topicT t; +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_ids, +(select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, +(select array_agg(p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_topic_id = t.id) as projection_ids +FROM topic_t t; CREATE OR REPLACE VIEW motion AS SELECT *, -(select array_agg(m1.id) from motionT m1 where m1.lead_motion_id = m.id) as amendment_ids, -(select array_agg(m1.id) from motionT m1 where m1.sort_parent_id = m.id) as sort_child_ids, -(select array_agg(m1.id) from motionT m1 where m1.origin_id = m.id) as derived_motion_ids, -(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_derived_motion_id = m.id) as all_origin_ids, -(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motionT n where n.all_origin_id = m.id) as all_derived_motion_ids, -(select array_agg(g.id) from gm_motion_state_extension_reference_idsT g where g.motion_id = m.id) as state_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_state_extension_reference_idsT g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, -(select array_agg(g.id) from gm_motion_recommendation_extension_reference_idsT g where g.motion_id = m.id) as recommendation_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_idsT g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, -(select array_agg(ms.id) from motion_submitterT ms where ms.motion_id = m.id) as submitter_ids, -(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motionT n where n.motion_id = m.id) as supporter_meeting_user_ids, -(select array_agg(me.id) from motion_editorT me where me.motion_id = m.id) as editor_ids, -(select array_agg(mw.id) from motion_working_group_speakerT mw where mw.motion_id = m.id) as working_group_speaker_ids, -(select array_agg(p.id) from pollT p where p.content_object_id_motion_id = m.id) as poll_ids, -(select array_agg(o.id) from optionT o where o.content_object_id_motion_id = m.id) as option_ids, -(select array_agg(mc.id) from motion_change_recommendationT mc where mc.motion_id = m.id) as change_recommendation_ids, -(select array_agg(mc.id) from motion_commentT mc where mc.motion_id = m.id) as comment_ids, -(select a.id from agenda_itemT a where a.content_object_id_motion_id = m.id) as agenda_item_id, -(select l.id from list_of_speakersT l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_motion_id = m.id) as attachment_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_motion_id = m.id) as projection_ids, -(select array_agg(p.id) from personal_noteT p where p.content_object_id_motion_id = m.id) as personal_note_ids -FROM motionT m; +(select array_agg(m1.id) from motion_t m1 where m1.lead_motion_id = m.id) as amendment_ids, +(select array_agg(m1.id) from motion_t m1 where m1.sort_parent_id = m.id) as sort_child_ids, +(select array_agg(m1.id) from motion_t m1 where m1.origin_id = m.id) as derived_motion_ids, +(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_derived_motion_id = m.id) as all_origin_ids, +(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_agg(g.id) from gm_motion_state_extension_reference_ids_t g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids_t g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.id) from gm_motion_recommendation_extension_reference_ids_t g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_ids_t g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, +(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, +(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, +(select array_agg(p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, +(select array_agg(o.id) from option_t o where o.content_object_id_motion_id = m.id) as option_ids, +(select array_agg(mc.id) from motion_change_recommendation_t mc where mc.motion_id = m.id) as change_recommendation_ids, +(select array_agg(mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, +(select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, +(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, +(select array_agg(p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids +FROM motion_t m; CREATE OR REPLACE VIEW motion_comment_section AS SELECT *, -(select array_agg(mc.id) from motion_commentT mc where mc.section_id = m.id) as comment_ids, -(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_sectionT n where n.motion_comment_section_id = m.id) as read_group_ids, -(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_sectionT n where n.motion_comment_section_id = m.id) as write_group_ids -FROM motion_comment_sectionT m; +(select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, +(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as read_group_ids, +(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as write_group_ids +FROM motion_comment_section_t m; CREATE OR REPLACE VIEW motion_category AS SELECT *, -(select array_agg(mc.id) from motion_categoryT mc where mc.parent_id = m.id) as child_ids, -(select array_agg(m1.id) from motionT m1 where m1.category_id = m.id) as motion_ids -FROM motion_categoryT m; +(select array_agg(mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, +(select array_agg(m1.id) from motion_t m1 where m1.category_id = m.id) as motion_ids +FROM motion_category_t m; CREATE OR REPLACE VIEW motion_block AS SELECT *, -(select array_agg(m1.id) from motionT m1 where m1.block_id = m.id) as motion_ids, -(select a.id from agenda_itemT a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, -(select l.id from list_of_speakersT l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, -(select array_agg(p.id) from projectionT p where p.content_object_id_motion_block_id = m.id) as projection_ids -FROM motion_blockT m; +(select array_agg(m1.id) from motion_t m1 where m1.block_id = m.id) as motion_ids, +(select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, +(select array_agg(p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids +FROM motion_block_t m; CREATE OR REPLACE VIEW motion_state AS SELECT *, -(select array_agg(ms.id) from motion_stateT ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, -(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_stateT n where n.previous_state_id = m.id) as next_state_ids, -(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_stateT n where n.next_state_id = m.id) as previous_state_ids, -(select array_agg(m1.id) from motionT m1 where m1.state_id = m.id) as motion_ids, -(select array_agg(m1.id) from motionT m1 where m1.recommendation_id = m.id) as motion_recommendation_ids, -(select mw.id from motion_workflowT mw where mw.first_state_id = m.id) as first_state_of_workflow_id -FROM motion_stateT m; +(select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, +(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.previous_state_id = m.id) as next_state_ids, +(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.next_state_id = m.id) as previous_state_ids, +(select array_agg(m1.id) from motion_t m1 where m1.state_id = m.id) as motion_ids, +(select array_agg(m1.id) from motion_t m1 where m1.recommendation_id = m.id) as motion_recommendation_ids, +(select mw.id from motion_workflow_t mw where mw.first_state_id = m.id) as first_state_of_workflow_id +FROM motion_state_t m; CREATE OR REPLACE VIEW motion_workflow AS SELECT *, -(select array_agg(ms.id) from motion_stateT ms where ms.workflow_id = m.id) as state_ids, -(select m1.id from meetingT m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, -(select m1.id from meetingT m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id, -(select m1.id from meetingT m1 where m1.motions_default_statute_amendment_workflow_id = m.id) as default_statute_amendment_workflow_meeting_id -FROM motion_workflowT m; +(select array_agg(ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, +(select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, +(select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id, +(select m1.id from meeting_t m1 where m1.motions_default_statute_amendment_workflow_id = m.id) as default_statute_amendment_workflow_meeting_id +FROM motion_workflow_t m; CREATE OR REPLACE VIEW motion_statute_paragraph AS SELECT *, -(select array_agg(m1.id) from motionT m1 where m1.statute_paragraph_id = m.id) as motion_ids -FROM motion_statute_paragraphT m; +(select array_agg(m1.id) from motion_t m1 where m1.statute_paragraph_id = m.id) as motion_ids +FROM motion_statute_paragraph_t m; CREATE OR REPLACE VIEW poll AS SELECT *, -(select array_agg(o.id) from optionT o where o.poll_id = p.id) as option_ids, -(select array_agg(n.user_id) from nm_poll_voted_ids_userT n where n.poll_id = p.id) as voted_ids, -(select array_agg(n.group_id) from nm_group_poll_ids_pollT n where n.poll_id = p.id) as entitled_group_ids, -(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_poll_id = p.id) as projection_ids -FROM pollT p; +(select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, +(select array_agg(n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_poll_id = p.id) as projection_ids +FROM poll_t p; CREATE OR REPLACE VIEW option AS SELECT *, -(select p.id from pollT p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, -(select array_agg(v.id) from voteT v where v.option_id = o.id) as vote_ids -FROM optionT o; +(select p.id from poll_t p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, +(select array_agg(v.id) from vote_t v where v.option_id = o.id) as vote_ids +FROM option_t o; CREATE OR REPLACE VIEW assignment AS SELECT *, -(select array_agg(ac.id) from assignment_candidateT ac where ac.assignment_id = a.id) as candidate_ids, -(select array_agg(p.id) from pollT p where p.content_object_id_assignment_id = a.id) as poll_ids, -(select ai.id from agenda_itemT ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, -(select l.id from list_of_speakersT l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_idsT g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_idsT g where g.attachment_id_assignment_id = a.id) as attachment_ids, -(select array_agg(p.id) from projectionT p where p.content_object_id_assignment_id = a.id) as projection_ids -FROM assignmentT a; +(select array_agg(ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, +(select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, +(select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, +(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_ids, +(select array_agg(p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids +FROM assignment_t a; CREATE OR REPLACE VIEW poll_candidate_list AS SELECT *, -(select array_agg(pc.id) from poll_candidateT pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, -(select o.id from optionT o where o.content_object_id_poll_candidate_list_id = p.id) as option_id -FROM poll_candidate_listT p; +(select array_agg(pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, +(select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id +FROM poll_candidate_list_t p; CREATE OR REPLACE VIEW mediafile AS SELECT *, -(select array_agg(n.group_id) from nm_group_mediafile_inherited_access_group_ids_mediafileT n where n.mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafileT n where n.mediafile_id = m.id) as access_group_ids, -(select array_agg(m1.id) from mediafileT m1 where m1.parent_id = m.id) as child_ids, -(select l.id from list_of_speakersT l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, -(select array_agg(p.id) from projectionT p where p.content_object_id_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_mediafile_attachment_idsT g where g.mediafile_id = m.id) as attachment_ids, -(select m1.id from meetingT m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, -(select m1.id from meetingT m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, -(select m1.id from meetingT m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id -FROM mediafileT m; +(select array_agg(n.group_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as access_group_ids, +(select array_agg(m1.id) from mediafile_t m1 where m1.parent_id = m.id) as child_ids, +(select l.id from list_of_speakers_t l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, +(select array_agg(p.id) from projection_t p where p.content_object_id_mediafile_id = m.id) as projection_ids, +(select array_agg(g.id) from gm_mediafile_attachment_ids_t g where g.mediafile_id = m.id) as attachment_ids, +(select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id +FROM mediafile_t m; CREATE OR REPLACE VIEW projector AS SELECT *, -(select array_agg(p1.id) from projectionT p1 where p1.current_projector_id = p.id) as current_projection_ids, -(select array_agg(p1.id) from projectionT p1 where p1.preview_projector_id = p.id) as preview_projection_ids, -(select array_agg(p1.id) from projectionT p1 where p1.history_projector_id = p.id) as history_projection_ids, -(select m.id from meetingT m where m.reference_projector_id = p.id) as used_as_reference_projector_meeting_id -FROM projectorT p; +(select array_agg(p1.id) from projection_t p1 where p1.current_projector_id = p.id) as current_projection_ids, +(select array_agg(p1.id) from projection_t p1 where p1.preview_projector_id = p.id) as preview_projection_ids, +(select array_agg(p1.id) from projection_t p1 where p1.history_projector_id = p.id) as history_projection_ids, +(select m.id from meeting_t m where m.reference_projector_id = p.id) as used_as_reference_projector_meeting_id +FROM projector_t p; CREATE OR REPLACE VIEW projector_message AS SELECT *, -(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids -FROM projector_messageT p; +(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids +FROM projector_message_t p; CREATE OR REPLACE VIEW projector_countdown AS SELECT *, -(select array_agg(p1.id) from projectionT p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, -(select m.id from meetingT m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, -(select m.id from meetingT m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id -FROM projector_countdownT p; +(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, +(select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id +FROM projector_countdown_t p; CREATE OR REPLACE VIEW chat_group AS SELECT *, -(select array_agg(cm.id) from chat_messageT cm where cm.chat_group_id = c.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_chat_group_read_group_ids_groupT n where n.chat_group_id = c.id) as read_group_ids, -(select array_agg(n.group_id) from nm_chat_group_write_group_ids_groupT n where n.chat_group_id = c.id) as write_group_ids -FROM chat_groupT c; +(select array_agg(cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, +(select array_agg(n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids +FROM chat_group_t c; -- Alter table relations -ALTER TABLE organizationT ADD FOREIGN KEY(theme_id) REFERENCES themeT(id) INITIALLY DEFERRED; - -ALTER TABLE meeting_userT ADD FOREIGN KEY(user_id) REFERENCES userT(id); -ALTER TABLE meeting_userT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); -ALTER TABLE meeting_userT ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_userT(id); - -ALTER TABLE committeeT ADD FOREIGN KEY(default_meeting_id) REFERENCES meetingT(id); -ALTER TABLE committeeT ADD FOREIGN KEY(forwarding_user_id) REFERENCES userT(id); - -ALTER TABLE meetingT ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organizationT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organizationT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(template_for_organization_id) REFERENCES organizationT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_regular_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_italic_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafileT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(committee_id) REFERENCES committeeT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(reference_projector_id) REFERENCES projectorT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdownT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdownT(id); -ALTER TABLE meetingT ADD FOREIGN KEY(default_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; -ALTER TABLE meetingT ADD FOREIGN KEY(admin_group_id) REFERENCES groupT(id) INITIALLY DEFERRED; - -ALTER TABLE structure_levelT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE groupT ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE groupT ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE groupT ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE groupT ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE groupT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; - -ALTER TABLE personal_noteT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE personal_noteT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE personal_noteT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE tagT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); -ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); -ALTER TABLE agenda_itemT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); -ALTER TABLE agenda_itemT ADD FOREIGN KEY(parent_id) REFERENCES agenda_itemT(id); -ALTER TABLE agenda_itemT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafileT(id); -ALTER TABLE list_of_speakersT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(structure_level_id) REFERENCES structure_levelT(id); -ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakersT(id); -ALTER TABLE structure_level_list_of_speakersT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE point_of_order_categoryT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE speakerT ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakersT(id); -ALTER TABLE speakerT ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakersT(id); -ALTER TABLE speakerT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE speakerT ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_categoryT(id); -ALTER TABLE speakerT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE topicT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motionT ADD FOREIGN KEY(lead_motion_id) REFERENCES motionT(id); -ALTER TABLE motionT ADD FOREIGN KEY(sort_parent_id) REFERENCES motionT(id); -ALTER TABLE motionT ADD FOREIGN KEY(origin_id) REFERENCES motionT(id); -ALTER TABLE motionT ADD FOREIGN KEY(origin_meeting_id) REFERENCES meetingT(id); -ALTER TABLE motionT ADD FOREIGN KEY(state_id) REFERENCES motion_stateT(id); -ALTER TABLE motionT ADD FOREIGN KEY(recommendation_id) REFERENCES motion_stateT(id); -ALTER TABLE motionT ADD FOREIGN KEY(category_id) REFERENCES motion_categoryT(id); -ALTER TABLE motionT ADD FOREIGN KEY(block_id) REFERENCES motion_blockT(id); -ALTER TABLE motionT ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraphT(id); -ALTER TABLE motionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE motion_submitterT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); -ALTER TABLE motion_submitterT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_editorT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE motion_editorT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); -ALTER TABLE motion_editorT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); -ALTER TABLE motion_working_group_speakerT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_commentT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); -ALTER TABLE motion_commentT ADD FOREIGN KEY(section_id) REFERENCES motion_comment_sectionT(id); -ALTER TABLE motion_commentT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_comment_sectionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_categoryT ADD FOREIGN KEY(parent_id) REFERENCES motion_categoryT(id); -ALTER TABLE motion_categoryT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_blockT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_change_recommendationT ADD FOREIGN KEY(motion_id) REFERENCES motionT(id); -ALTER TABLE motion_change_recommendationT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE motion_stateT ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_stateT(id); -ALTER TABLE motion_stateT ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflowT(id) INITIALLY DEFERRED; -ALTER TABLE motion_stateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; - -ALTER TABLE motion_workflowT ADD FOREIGN KEY(first_state_id) REFERENCES motion_stateT(id) INITIALLY DEFERRED; -ALTER TABLE motion_workflowT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; - -ALTER TABLE motion_statute_paragraphT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); -ALTER TABLE pollT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); -ALTER TABLE pollT ADD FOREIGN KEY(global_option_id) REFERENCES optionT(id); -ALTER TABLE pollT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE optionT ADD FOREIGN KEY(poll_id) REFERENCES pollT(id); -ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_user_id) REFERENCES userT(id); -ALTER TABLE optionT ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_listT(id); -ALTER TABLE optionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE voteT ADD FOREIGN KEY(option_id) REFERENCES optionT(id); -ALTER TABLE voteT ADD FOREIGN KEY(user_id) REFERENCES userT(id); -ALTER TABLE voteT ADD FOREIGN KEY(delegated_user_id) REFERENCES userT(id); -ALTER TABLE voteT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE assignmentT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE assignment_candidateT ADD FOREIGN KEY(assignment_id) REFERENCES assignmentT(id); -ALTER TABLE assignment_candidateT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE assignment_candidateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE poll_candidate_listT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE poll_candidateT ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_listT(id); -ALTER TABLE poll_candidateT ADD FOREIGN KEY(user_id) REFERENCES userT(id); -ALTER TABLE poll_candidateT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE mediafileT ADD FOREIGN KEY(parent_id) REFERENCES mediafileT(id); -ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meetingT(id); -ALTER TABLE mediafileT ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organizationT(id); - -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_list_of_speakers_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_current_los_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_amendment_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_block_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_assignment_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_mediafile_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_message_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_countdown_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_assignment_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_motion_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; -ALTER TABLE projectorT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id) INITIALLY DEFERRED; - -ALTER TABLE projectionT ADD FOREIGN KEY(current_projector_id) REFERENCES projectorT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(preview_projector_id) REFERENCES projectorT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(history_projector_id) REFERENCES projectorT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meetingT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motionT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafileT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakersT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_blockT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignmentT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_itemT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topicT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES pollT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_messageT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdownT(id); -ALTER TABLE projectionT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE projector_messageT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE projector_countdownT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE chat_groupT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); - -ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_userT(id); -ALTER TABLE chat_messageT ADD FOREIGN KEY(chat_group_id) REFERENCES chat_groupT(id); -ALTER TABLE chat_messageT ADD FOREIGN KEY(meeting_id) REFERENCES meetingT(id); +ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; + +ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); +ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id); + +ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id); +ALTER TABLE committee_t ADD FOREIGN KEY(forwarding_user_id) REFERENCES user_t(id); + +ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES organization_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafile_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(committee_id) REFERENCES committee_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(reference_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdown_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdown_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(default_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(admin_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; + +ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE group_t ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id); +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id); +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id); +ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id); +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id); +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_t ADD FOREIGN KEY(lead_motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(sort_parent_id) REFERENCES motion_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(origin_id) REFERENCES motion_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(origin_meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(state_id) REFERENCES motion_state_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(recommendation_id) REFERENCES motion_state_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraph_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_comment_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_comment_t ADD FOREIGN KEY(section_id) REFERENCES motion_comment_section_t(id); +ALTER TABLE motion_comment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_comment_section_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id); +ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE motion_state_t ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_state_t(id); +ALTER TABLE motion_state_t ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE motion_statute_paragraph_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); +ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id); +ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id); +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id); +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id); +ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id); +ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); +ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id); +ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id); +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id); +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id); +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id); +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id); + +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_list_of_speakers_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_current_los_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_amendment_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_block_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_assignment_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_mediafile_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_message_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_countdown_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_assignment_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE projector_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); + +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); +ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id); +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); -- Create trigger --- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projectorT.used_as_default_projector_for_agenda_item_list_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); --- definition trigger not null for meeting.default_projector_topic_ids against projectorT.used_as_default_projector_for_topic_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_topic_ids against projector_t.used_as_default_projector_for_topic_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); --- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projectorT.used_as_default_projector_for_list_of_speakers_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projector_t.used_as_default_projector_for_list_of_speakers_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); --- definition trigger not null for meeting.default_projector_current_list_of_speakers_ids against projectorT.used_as_default_projector_for_current_los_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_list_of_speakers_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_current_list_of_speakers_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_list_of_speakers_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_ids against projectorT.used_as_default_projector_for_motion_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_motion_ids against projector_t.used_as_default_projector_for_motion_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); --- definition trigger not null for meeting.default_projector_amendment_ids against projectorT.used_as_default_projector_for_amendment_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_amendment_ids against projector_t.used_as_default_projector_for_amendment_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_block_ids against projectorT.used_as_default_projector_for_motion_block_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_motion_block_ids against projector_t.used_as_default_projector_for_motion_block_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); --- definition trigger not null for meeting.default_projector_assignment_ids against projectorT.used_as_default_projector_for_assignment_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_assignment_ids against projector_t.used_as_default_projector_for_assignment_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); --- definition trigger not null for meeting.default_projector_mediafile_ids against projectorT.used_as_default_projector_for_mediafile_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_mediafile_ids against projector_t.used_as_default_projector_for_mediafile_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); --- definition trigger not null for meeting.default_projector_message_ids against projectorT.used_as_default_projector_for_message_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_message_ids against projector_t.used_as_default_projector_for_message_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); --- definition trigger not null for meeting.default_projector_countdown_ids against projectorT.used_as_default_projector_for_countdown_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_countdown_ids against projector_t.used_as_default_projector_for_countdown_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); --- definition trigger not null for meeting.default_projector_assignment_poll_ids against projectorT.used_as_default_projector_for_assignment_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_assignment_poll_ids against projector_t.used_as_default_projector_for_assignment_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_poll_ids against projectorT.used_as_default_projector_for_motion_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_motion_poll_ids against projector_t.used_as_default_projector_for_motion_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); --- definition trigger not null for meeting.default_projector_poll_ids against projectorT.used_as_default_projector_for_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON projectorT INITIALLY DEFERRED +-- definition trigger not null for meeting.default_projector_poll_ids against projector_t.used_as_default_projector_for_poll_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projectorT +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 4ccba1c5..b1faa1a8 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -64,8 +64,8 @@ def wrapper(*args, **kwargs) -> str: # type:ignore @staticmethod @max_length def get_table_name(table_name: str) -> str: - """get's the table name as ol dcollection name with appendis 'T'""" - return table_name + "T" + """get's the table name as old collection name with appendix '_t'""" + return table_name + "_t" @staticmethod @max_length From 5a9094c46130dbe71ced6caf0aee14a569a8aa63 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Tue, 9 Apr 2024 12:10:28 +0200 Subject: [PATCH 044/142] sql only in relation, better error reporting --- dev/sql/schema_relational.sql | 58 +++++---- dev/src/generate_sql_schema.py | 178 +++++++++++++++++----------- dev/tests/base.py | 34 +++--- dev/tests/test_generic_relations.py | 64 +++++----- models.yml | 25 ++-- 5 files changed, 206 insertions(+), 153 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 641e24fa..54868289 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '439bb5c7ec340920a013cfb408687436' +-- MODELS_YML_CHECKSUM = 'a5825cc3e8727870c460309635849354' -- Type definitions DO $$ BEGIN @@ -482,7 +482,7 @@ comment on column organization_t.limit_of_users is 'Maximum of active users for /* Fields without SQL definition for table organization - vote_decrypt_public_main_key type:string is marked as a calculated field + organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema */ @@ -506,7 +506,6 @@ CREATE TABLE IF NOT EXISTS user_t ( is_demo_user boolean, last_login timestamptz, organization_management_level enum_user_organization_management_level, - committee_ids integer[], meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -515,7 +514,6 @@ CREATE TABLE IF NOT EXISTS user_t ( comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; -comment on column user_t.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; comment on column user_t.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; @@ -604,7 +602,6 @@ CREATE TABLE IF NOT EXISTS committee_t ( description text, external_id varchar(256), default_meeting_id integer, - user_ids integer[], forwarding_user_id integer, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -612,7 +609,6 @@ CREATE TABLE IF NOT EXISTS committee_t ( comment on column committee_t.external_id is 'unique'; -comment on column committee_t.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; CREATE TABLE IF NOT EXISTS meeting_t ( @@ -1212,7 +1208,7 @@ comment on column poll_t.votes_signature is 'base64 signature of votes_raw field /* Fields without SQL definition for table poll - vote_count type:number is marked as a calculated field + poll/vote_count: type:number is marked as a calculated field and not generated in schema */ @@ -1395,7 +1391,7 @@ CREATE TABLE IF NOT EXISTS projection_t ( /* Fields without SQL definition for table projection - content type:JSON is marked as a calculated field + projection/content: type:JSON is marked as a calculated field and not generated in schema */ @@ -1485,12 +1481,18 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id), tagged_id varchar(100) NOT NULL, - tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committeeT(id), - tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meetingT(id), + tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id), + tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id), CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); +CREATE TABLE IF NOT EXISTS nm_committee_user_ids_user_t ( + committee_id integer NOT NULL REFERENCES committee_t (id), + user_id integer NOT NULL REFERENCES user_t (id), + PRIMARY KEY (committee_id, user_id) +); + CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), @@ -1549,9 +1551,9 @@ CREATE TABLE IF NOT EXISTS gm_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, tag_id integer NOT NULL REFERENCES tag_t(id), tagged_id varchar(100) NOT NULL, - tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_itemT(id), - tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), - tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id), + tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id), + tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); @@ -1566,7 +1568,7 @@ CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), state_extension_reference_id varchar(100) NOT NULL, - state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); @@ -1575,7 +1577,7 @@ CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), recommendation_extension_reference_id varchar(100) NOT NULL, - recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), + recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); @@ -1596,9 +1598,9 @@ CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, mediafile_id integer NOT NULL REFERENCES mediafile_t(id), attachment_id varchar(100) NOT NULL, - attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motionT(id), - attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topicT(id), - attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignmentT(id), + attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), + attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id), + attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id), CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), CONSTRAINT unique_$mediafile_id_$attachment_id UNIQUE (mediafile_id, attachment_id) ); @@ -1617,7 +1619,7 @@ CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_group_t ( -- View definitions CREATE OR REPLACE VIEW organization AS SELECT *, -(select array_agg(c.id) from committeeT c) as committee_ids, +(select array_agg(c.id) from committee_t c) as committee_ids, (select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, (select array_agg(m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, @@ -1630,6 +1632,7 @@ FROM organization_t o; CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.committee_id) from nm_committee_user_ids_user_t n where n.user_id = u.id) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, @@ -1640,6 +1643,7 @@ CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids FROM user_t u; +comment on column user_.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; CREATE OR REPLACE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, @@ -1668,12 +1672,14 @@ FROM theme_t t; CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, +(select array_agg(n.user_id) from nm_committee_user_ids_user_t n where n.committee_id = c.id) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, (select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, (select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; +comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, @@ -1758,6 +1764,7 @@ CREATE OR REPLACE VIEW group_ AS SELECT *, (select array_agg(n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids FROM group_t g; +comment on column group_.mediafile_inherited_access_group_ids is 'Calculated field.'; CREATE OR REPLACE VIEW tag AS SELECT *, (select array_agg(g.id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids @@ -1924,6 +1931,7 @@ CREATE OR REPLACE VIEW mediafile AS SELECT *, (select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id FROM mediafile_t m; +comment on column mediafile.inherited_access_group_ids is 'Calculated field.'; CREATE OR REPLACE VIEW projector AS SELECT *, (select array_agg(p1.id) from projection_t p1 where p1.current_projector_id = p.id) as current_projection_ids, @@ -2287,7 +2295,7 @@ Model.Field -> Model.Field */ /* -SQL nrs: => organization/committee_ids:-> committee/ +SQL nr: => organization/committee_ids:-> committee/ SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id @@ -2298,6 +2306,7 @@ SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id SQL nr: => organization/user_ids:-> user/ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids +SQL nt:nt => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1r => user/forwarding_committee_ids:-> committee/forwarding_user_id SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id @@ -2328,6 +2337,7 @@ SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r: => committee/default_meeting_id:-> meeting/ +SQL nt:nt => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids @@ -2682,5 +2692,11 @@ FIELD 1rR: => chat_message/chat_group_id:-> chat_group/ FIELD 1rR: => chat_message/meeting_id:-> meeting/ */ +/* +There are 3 errors/warnings + organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema + poll/vote_count: type:number is marked as a calculated field and not generated in schema + projection/content: type:JSON is marked as a calculated field and not generated in schema +*/ -/* Missing attribute handling for constant, sql, on_delete, equal_fields, unique, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, on_delete, sqlTODO, sql, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index fdf6ee5d..8ed8d80a 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -26,11 +26,13 @@ class SchemaZoneTexts(TypedDict, total=False): table: str view: str + post_view: str alter_table: str alter_table_final: str create_trigger: str undecided: str final_info: str + errors: list[str] class ToDict(TypedDict): @@ -76,7 +78,9 @@ class GenerateCodeBlocks: ) # Key=Name, data: collected content of table @classmethod - def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str]: + def generate_the_code( + cls, + ) -> tuple[str, str, str, str, str, list[str], str, str, list[str]]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -89,6 +93,7 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field create_trigger_code Definitions of triggers + errors: to show """ handled_attributes = { "required", @@ -118,11 +123,12 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str final_info_code: str = "" missing_handled_attributes = [] im_table_code = "" + errors: list[str] = [] for table_name, fields in MODELS.items(): if table_name in ["_migration_index", "_meta"]: continue - schema_zone_texts: SchemaZoneTexts = defaultdict(str) # type: ignore + schema_zone_texts = cast(SchemaZoneTexts, defaultdict(str)) cls.intermediate_tables = {} for fname, fdata in fields.items(): @@ -134,7 +140,9 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str missing_handled_attributes.append(attr) method_or_str, type_ = cls.get_method(fname, fdata) if isinstance(method_or_str, str): - schema_zone_texts["undecided"] += method_or_str + error = Helper.prefix_error(method_or_str, table_name, fname) + schema_zone_texts["undecided"] += error + errors.append(error) else: if (enum_ := fdata.get("enum")) or ( enum_ := fdata.get("items", {}).get("enum") @@ -142,9 +150,11 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str pre_code += Helper.get_enum_type_definition( table_name, fname, enum_ ) - result = method_or_str(table_name, fname, fdata, type_) + result, error = method_or_str(table_name, fname, fdata, type_) for k, v in result.items(): schema_zone_texts[k] += v or "" # type: ignore + if error: + errors.append(Helper.prefix_error(error, table_name, fname)) if code := schema_zone_texts["table"]: table_name_code += Helper.get_table_head(table_name) @@ -156,6 +166,8 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str if code := schema_zone_texts["view"]: view_name_code += Helper.get_view_head(table_name) view_name_code += Helper.get_view_body_end(table_name, code) + if code := schema_zone_texts["post_view"]: + view_name_code += code if code := schema_zone_texts["alter_table_final"]: alter_table_final_code += code + "\n" if code := schema_zone_texts["create_trigger"]: @@ -174,15 +186,21 @@ def generate_the_code(cls) -> tuple[str, str, str, str, str, list[str], str, str missing_handled_attributes, im_table_code, create_trigger_code, + errors, ) @classmethod def get_method( cls, fname: str, fdata: dict[str, Any] - ) -> tuple[str | Callable[..., SchemaZoneTexts], str]: + ) -> tuple[str | Callable[..., tuple[SchemaZoneTexts, str]], str]: + """ + returns + - string or a callable with return value of type SchemaZoneTexts + - type as string + """ if fdata.get("calculated"): return ( - f" {fname} type:{fdata.get('type')} is marked as a calculated field\n", + f"type:{fdata.get('type')} is marked as a calculated field and not generated in schema\n", "", ) if fname == "id": @@ -200,14 +218,15 @@ def get_method( text = "no method defined" else: text = "Unknown Type" - return (f" {fname} type:{fdata.get('type')} {text}\n", type_) + return (f"type:{type_} {text}\n", type_) @classmethod def get_schema_simple_types( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts - subst, text = Helper.get_initials(table_name, fname, type_, fdata) + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) + subst, szt = Helper.get_initials(table_name, fname, type_, fdata) + text.update(szt) if isinstance((tmp := subst["type"]), string.Template): if maxLength := fdata.get("maxLength"): tmp = tmp.substitute({"maxLength": maxLength}) @@ -217,58 +236,63 @@ def get_schema_simple_types( tmp = tmp.substitute({"maxLength": 256}) subst["type"] = tmp text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) - return text + return text, "" @classmethod def get_schema_color( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts - subst, text = Helper.get_initials(table_name, fname, type_, fdata) + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) + subst, tmp = Helper.get_initials(table_name, fname, type_, fdata) + text.update(tmp) tmpl = FIELD_TYPES[type_]["pg_type"] subst["type"] = tmpl.substitute(subst) if default := fdata.get("default"): subst["default"] = f" DEFAULT {int(default[1:], 16)}" text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) - return text + return text, "" @classmethod def get_schema_primary_key( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts - subst, text = Helper.get_initials(table_name, fname, type_, fdata) + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) + subst, tmp = Helper.get_initials(table_name, fname, type_, fdata) + text.update(tmp) subst["primary_key"] = " PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY" text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) - return text + return text, "" @classmethod def get_schema_organization_id( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts - subst, text = Helper.get_initials(table_name, fname, type_, fdata) + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) + subst, tmp = Helper.get_initials(table_name, fname, type_, fdata) + text.update(tmp) subst["primary_key"] = " GENERATED ALWAYS AS (1) STORED" text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) - return text + return text, "" @classmethod def get_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts = {} + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_field: TableFieldType = ( TableFieldType.get_definitions_from_foreign( fdata.get("to"), fdata.get("reference") ) ) - state, primary, final_info, error = Helper.check_relation_definitions( + state, _, final_info, error = Helper.check_relation_definitions( own_table_field, [foreign_table_field] ) if state == FieldSqlErrorType.FIELD: - text.update(cls.get_schema_simple_types(table_name, fname, fdata, "number")) + text, error = cls.get_schema_simple_types( + table_name, fname, fdata, "number" + ) initially_deferred = fdata.get( "deferred" ) or ModelsHelper.is_fk_initially_deferred( @@ -303,7 +327,7 @@ def get_relation_type( cast(str, foreign_table_field.column), ) text["final_info"] = final_info - return text + return text, error @classmethod def get_sql_for_relation_1_1( @@ -323,8 +347,8 @@ def get_sql_for_relation_1_1( @classmethod def get_relation_list_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts = {} + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_field: TableFieldType = ( TableFieldType.get_definitions_from_foreign( @@ -407,6 +431,10 @@ def get_relation_list_type( foreign_table_column, foreign_table_ref_column, ) + if comment := fdata.get("description"): + text["post_view"] = Helper.get_post_view_comment( + HelperGetNames.get_view_name(table_name), fname, comment + ) if own_table_field.field_def.get("required"): text["create_trigger"] = ( cls.get_trigger_check_not_null_for_relation_lists( @@ -417,7 +445,7 @@ def get_relation_list_type( ) ) text["final_info"] = final_info - return text + return text, error @classmethod def get_sql_for_relation_n_1( @@ -458,7 +486,7 @@ def get_trigger_check_not_null_for_relation_lists( @classmethod def get_generic_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: + ) -> tuple[SchemaZoneTexts, str]: text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_fields: list[TableFieldType] = ( @@ -467,13 +495,13 @@ def get_generic_relation_type( ) ) - state, primary, final_info, error = Helper.check_relation_definitions( + state, _, final_info, error = Helper.check_relation_definitions( own_table_field, foreign_table_fields ) if state == FieldSqlErrorType.FIELD: - text.update( - cls.get_schema_simple_types(table_name, fname, fdata, fdata["type"]) + text, error = cls.get_schema_simple_types( + table_name, fname, fdata, fdata["type"] ) initially_deferred = any( ModelsHelper.is_fk_initially_deferred( @@ -503,13 +531,13 @@ def get_generic_relation_type( own_table_field.column, foreign_tables ) text["final_info"] = final_info - return text + return text, error @classmethod def get_generic_relation_list_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str - ) -> SchemaZoneTexts: - text: SchemaZoneTexts = {} + ) -> tuple[SchemaZoneTexts, str]: + text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_fields: list[TableFieldType] = ( ModelsHelper.get_definitions_from_foreign_list( @@ -522,13 +550,16 @@ def get_generic_relation_list_type( if state == FieldSqlErrorType.SQL and primary: # create gm-intermediate table - gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( - own_table_field, foreign_table_fields - ) - if gm_foreign_table not in cls.intermediate_tables: - cls.intermediate_tables[gm_foreign_table] = value - else: - raise Exception(f"Tried to create gm_table '{gm_foreign_table}' twice") + if primary: + gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( + own_table_field, foreign_table_fields + ) + if gm_foreign_table not in cls.intermediate_tables: + cls.intermediate_tables[gm_foreign_table] = value + else: + raise Exception( + f"Tried to create gm_table '{gm_foreign_table}' twice" + ) # add field to view definition of table_name text["view"] = cls.get_sql_for_relation_n_1( @@ -539,24 +570,13 @@ def get_generic_relation_list_type( f"{own_table_field.table}_{own_table_field.ref_column}", own_table_field.ref_column, ) + if comment := fdata.get("description"): + text["post_view"] += Helper.get_post_view_comment( + HelperGetNames.get_view_name(table_name), fname, comment + ) - # # add foreign key constraints for nm-relation-tables - # initially_deferred = any( - # ModelsHelper.is_fk_initially_deferred( - # table_name, foreign_table_field.table - # ) - # for foreign_table_field in foreign_table_fields - # ) - # for foreign_table_field in foreign_table_fields: - # text["alter_table_final"] = Helper.get_foreign_key_table_constraint_as_alter_table( - # table_name, - # foreign_table_field.table, - # fname[:-1], - # foreign_table_field.ref_column, - # initially_deferred, - # ) text["final_info"] = final_info - return text + return text, error class Helper: @@ -663,7 +683,7 @@ class Helper: ) ) GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( - " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_table_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}T(id)," + " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_view_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}(id)," ) RELATION_LIST_AGENDA = dedent( @@ -840,7 +860,8 @@ def get_gm_table_for_gm_nm_relation_lists( foreign_table_name = foreign_table_field.table subst_dict = { "own_table_column": own_table_column, - "foreign_table_name": foreign_table_name, + "foreign_table_name": HelperGetNames.get_table_name(foreign_table_name), + "foreign_view_name": foreign_table_name, "gm_content_field": HelperGetNames.get_gm_content_field( own_table_column, foreign_table_name ), @@ -874,7 +895,7 @@ def get_gm_table_for_gm_nm_relation_lists( def get_initials( table_name: str, fname: str, type_: str, fdata: dict[str, Any] ) -> tuple[SubstDict, SchemaZoneTexts]: - text: SchemaZoneTexts = {} + text = cast(SchemaZoneTexts, defaultdict(str)) flist: list[str] = [ cast(str, form[1]) for form in Formatter().parse(Helper.FIELD_TEMPLATE.template) @@ -914,18 +935,21 @@ def get_initials( f" CONSTRAINT {minlength_constraint_name} CHECK (char_length({fname}) >= {minLength})" ) if comment := fdata.get("description"): - text["alter_table"] = ( - f"comment on column {HelperGetNames.get_table_name(table_name)}.{fname} is '{comment}';\n" + text["alter_table"] = Helper.get_post_view_comment( + HelperGetNames.get_table_name(table_name), fname, comment ) return subst, text + @staticmethod + def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: + return f"comment on column {entity_name}.{fname} is '{comment}';\n" + @staticmethod def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: """ Returns - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) - string with error message or empty string if no error - """ error = "" field = field_all.field_def @@ -1060,15 +1084,14 @@ def generate_field_or_sql_decision( ("nt", "nGt"): (FieldSqlErrorType.SQL, False), ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), ("ntR", "1r"): (FieldSqlErrorType.SQL, False), + ("nts", "nts"): (FieldSqlErrorType.SQL, False), } state: FieldSqlErrorType | str | None primary: bool | str | None error = "" - state, primary = decision_list.get( - (own_c.rstrip("s"), foreign_c.rstrip("s")), (None, None) - ) + state, primary = decision_list.get((own_c, foreign_c), (None, None)) if state is None: error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" state = FieldSqlErrorType.ERROR @@ -1093,6 +1116,10 @@ def get_generic_field_constraint(own_column: str, foreign_tables: list[str]) -> constraint_name = HelperGetNames.get_generic_valid_constraint_name(own_column) return f""" CONSTRAINT {constraint_name} CHECK (split_part({own_column}, '/', 1) IN ('{"','".join(foreign_tables)}')),\n""" + @staticmethod + def prefix_error(method_or_str: str, table_name: str, fname: str) -> str: + return f" {table_name}/{fname}: {method_or_str}" + class ModelsHelper: @staticmethod @@ -1264,6 +1291,7 @@ def main() -> None: missing_handled_attributes, im_table_code, create_trigger_code, + errors, ) = GenerateCodeBlocks.generate_the_code() with open(DESTINATION, "w") as dest: dest.write(Helper.FILE_TEMPLATE) @@ -1284,10 +1312,18 @@ def main() -> None: dest.write("/*\n") dest.write(final_info_code) dest.write("*/\n") + if errors: + dest.write(f"/*\nThere are {len(errors)} errors/warnings\n") + dest.write("".join(errors)) + dest.write("*/\n") dest.write( f"\n/* Missing attribute handling for {', '.join(missing_handled_attributes)} */" ) - print(f"Models file {DESTINATION} successfully created.") + if errors: + print(f"Models file {DESTINATION} created with {len(errors)} errors/warnings\n") + print("".join(errors)) + else: + print(f"Models file {DESTINATION} successfully created.") if __name__ == "__main__": diff --git a/dev/tests/base.py b/dev/tests/base.py index 8878392e..58c37028 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -84,13 +84,13 @@ def populate_database(cls) -> None: """ do something like setting initial_data.json""" with cls.db_connection.transaction(): with cls.db_connection.cursor() as curs: - cls.theme1_id = DbUtils.insert_wrapper(curs, "themeT", { + cls.theme1_id = DbUtils.insert_wrapper(curs, "theme_t", { "name": "OpenSlides Blue", "accent_500": int("0x2196f3", 16), "primary_500": int("0x317796", 16), "warn_500": int("0xf06400", 16), }) - cls.organization_id = DbUtils.insert_wrapper(curs, "organizationT", { + cls.organization_id = DbUtils.insert_wrapper(curs, "organization_t", { "name": "Test Organization", "legal_notice": "OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.", "login_text": "Good Morning!", @@ -120,7 +120,7 @@ def populate_database(cls) -> None: "is_physical_person": "is_person" }) }) - cls.user1_id = DbUtils.insert_wrapper(curs, "userT", { + cls.user1_id = DbUtils.insert_wrapper(curs, "user_t", { "username": "admin", "last_name": "Administrator", "is_active": True, @@ -132,7 +132,7 @@ def populate_database(cls) -> None: "default_vote_weight": "1.000000", "organization_management_level": "superadmin", }) - cls.committee1_id = DbUtils.insert_wrapper(curs, "committeeT", { + cls.committee1_id = DbUtils.insert_wrapper(curs, "committee_t", { "name": "Default committee", "description": "Add description here", }) @@ -143,7 +143,7 @@ def populate_database(cls) -> None: cls.groupM1_staff_id = result_ids["staff_group_id"] cls.simple_workflowM1_id = result_ids["simple_workflow_id"] cls.complex_workflowM1_id = result_ids["complex_workflow_id"] - curs.execute("UPDATE committeeT SET default_meeting_id = %s where id = %s;", (result_ids["meeting_id"], cls.committee1_id)) + curs.execute("UPDATE committee_t SET default_meeting_id = %s where id = %s;", (result_ids["meeting_id"], cls.committee1_id)) @classmethod def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int = 0, ) -> None: @@ -161,14 +161,14 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int """ result = {} if meeting_id: - sequence_name = curs.execute("select * from pg_get_serial_sequence('meetingT', 'id');").fetchone()["pg_get_serial_sequence"] + sequence_name = curs.execute("select * from pg_get_serial_sequence('meeting_t', 'id');").fetchone()["pg_get_serial_sequence"] last_value = curs.execute(f"select last_value from {sequence_name};").fetchone()["last_value"] if last_value >= meeting_id: raise ValueError(f"meeting_id {meeting_id} is not available, last_value in sequence {sequence_name} is {last_value}") - result["meeting_id"] = curs.execute("select setval(pg_get_serial_sequence('meetingT', 'id'), %s);", (meeting_id,)).fetchone()["setval"] + result["meeting_id"] = curs.execute("select setval(pg_get_serial_sequence('meeting_t', 'id'), %s);", (meeting_id,)).fetchone()["setval"] else: - result["meeting_id"] = curs.execute("select nextval(pg_get_serial_sequence('meetingT', 'id')) as id_;").fetchone()["id_"] - (result["default_group_id"], result["admin_group_id"], result["staff_group_id"]) = DbUtils.insert_many_wrapper(curs, "groupT", [ + result["meeting_id"] = curs.execute("select nextval(pg_get_serial_sequence('meeting_t', 'id')) as id_;").fetchone()["id_"] + (result["default_group_id"], result["admin_group_id"], result["staff_group_id"]) = DbUtils.insert_many_wrapper(curs, "group_t", [ { "name": "Default", "permissions": [ @@ -211,7 +211,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "meeting_id": result["meeting_id"] } ]) - (result["default_projector_id"], result["secondary_projector_id"]) = DbUtils.insert_many_wrapper(curs, "projectorT", [ + (result["default_projector_id"], result["secondary_projector_id"]) = DbUtils.insert_many_wrapper(curs, "projector_t", [ { "name": "Default projector", "is_internal": False, @@ -271,9 +271,9 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "meeting_id": result["meeting_id"] } ]) - result["simple_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - result["complex_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflowT', 'id')) as new_id;").fetchone()["new_id"] - wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + result["simple_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;").fetchone()["new_id"] + result["complex_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;").fetchone()["new_id"] + wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_state_t", [ { "name": "submitted", "weight": 1, @@ -347,7 +347,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int }, ]) wf_m1_simple_first_state_id = wf_m1_simple_motion_state_ids[0] - wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_stateT", [ + wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_state_t", [ { "name": "in progress", "weight": 5, @@ -545,7 +545,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int ]) wf_m1_complex_first_state_id = wf_m1_complex_motion_state_ids[0] - DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_stateT", + DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_state_t", [ {"next_state_id": wf_m1_simple_motion_state_ids[1], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, {"next_state_id": wf_m1_simple_motion_state_ids[2], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, @@ -563,7 +563,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int {"next_state_id": wf_m1_complex_motion_state_ids[8], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, {"next_state_id": wf_m1_complex_motion_state_ids[9], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, ], returning='') - assert [result["simple_workflow_id"], result["complex_workflow_id"]] == DbUtils.insert_many_wrapper(curs, "motion_workflowT", + assert [result["simple_workflow_id"], result["complex_workflow_id"]] == DbUtils.insert_many_wrapper(curs, "motion_workflow_t", [ { "id": result["simple_workflow_id"], @@ -581,7 +581,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int } ] ) - assert result["meeting_id"] == DbUtils.insert_wrapper(curs, "meetingT", { + assert result["meeting_id"] == DbUtils.insert_wrapper(curs, "meeting_t", { "id": result["meeting_id"], "name": "OpenSlides Demo", "is_active_in_organization_id": cls.organization_id, diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 4243dc11..25247f31 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -60,7 +60,7 @@ def test_one_to_one_1tR_1t(self) -> None: assert group_staff_row["name"] == "Staff" assert group_staff_row["meeting_id"] == self.meeting1_id assert group_staff_row["default_group_for_meeting_id"] == None - curs.execute(sql.SQL("UPDATE meetingT SET default_group_id = %s where id = %s;"), (group_staff_row["id"], self.meeting1_id)) + curs.execute(sql.SQL("UPDATE meeting_t SET default_group_id = %s where id = %s;"), (group_staff_row["id"], self.meeting1_id)) # assert new and old data meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) assert meeting_row["default_group_id"] == group_staff_row["id"] @@ -77,7 +77,7 @@ def test_one_to_many_1t_ntR_update_error(self) -> None: with pytest.raises(psycopg.errors.RaiseException) as e: projector_id = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone()['id'] with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_id,)) + curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_id,)) assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) def test_one_to_many_1t_ntR_update_okay(self) -> None: @@ -85,8 +85,8 @@ def test_one_to_many_1t_ntR_update_okay(self) -> None: with self.db_connection.cursor() as curs: projector_ids = curs.execute("SELECT id from projector where meeting_id = %s", (self.meeting1_id,)).fetchall() with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) - curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) + curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) + curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) assert projector_ids[1]["id"] == DbUtils.select_id_wrapper(curs, 'meeting', self.meeting1_id, ['default_projector_topic_ids'])['default_projector_topic_ids'][0] def test_one_to_many_1t_ntR_update_wrong_update_sequence_error(self) -> None: @@ -95,8 +95,8 @@ def test_one_to_many_1t_ntR_update_wrong_update_sequence_error(self) -> None: projector_ids = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchall() with pytest.raises(psycopg.errors.RaiseException) as e: with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) - curs.execute(sql.SQL("UPDATE projectorT SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) + curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) + curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) def test_one_to_many_1t_ntR_delete_error(self) -> None: @@ -116,8 +116,8 @@ def test_one_to_many_1t_ntR_delete_insert_okay(self) -> None: field_list = ["meeting_id", "used_as_default_projector_for_agenda_item_list_in_meeting_id", "used_as_default_projector_for_topic_in_meeting_id", "used_as_default_projector_for_list_of_speakers_in_meeting_id", "used_as_default_projector_for_current_los_in_meeting_id", "used_as_default_projector_for_motion_in_meeting_id", "used_as_default_projector_for_amendment_in_meeting_id", "used_as_default_projector_for_motion_block_in_meeting_id", "used_as_default_projector_for_assignment_in_meeting_id", "used_as_default_projector_for_mediafile_in_meeting_id", "used_as_default_projector_for_message_in_meeting_id", "used_as_default_projector_for_countdown_in_meeting_id", "used_as_default_projector_for_assignment_poll_in_meeting_id", "used_as_default_projector_for_motion_poll_in_meeting_id", "used_as_default_projector_for_poll_in_meeting_id"] data = {fname: projector[fname] for fname in field_list} data["sequential_number"] = projector["sequential_number"] + 2 - new_projector_id = DbUtils.insert_wrapper(curs, "projectorT", data) - curs.execute(sql.SQL("UPDATE meetingT SET reference_projector_id = %s where id = %s;"), (new_projector_id, projector["meeting_id"])) + new_projector_id = DbUtils.insert_wrapper(curs, "projector_t", data) + curs.execute(sql.SQL("UPDATE meeting_t SET reference_projector_id = %s where id = %s;"), (new_projector_id, projector["meeting_id"])) curs.execute(sql.SQL("DELETE FROM projector where id = %s;"), (projector["id"],)) assert new_projector_id == cast(dict, DbUtils.select_id_wrapper(curs, "meeting", projector["meeting_id"], ["default_projector_topic_ids"]))["default_projector_topic_ids"][0] @@ -132,10 +132,10 @@ def test_generic_1GT_1tR(self) -> None: with self.db_connection.transaction(): pcl_id = 2 option_id = 3 - curs.execute("select setval(pg_get_serial_sequence('poll_candidate_listT', 'id'), %s);", (pcl_id,)) - assert pcl_id == DbUtils.insert_wrapper(curs, "poll_candidate_listT", {"id": pcl_id, "meeting_id": self.meeting1_id}) - curs.execute("select setval(pg_get_serial_sequence('optionT', 'id'), %s);", (option_id,)) - assert option_id == DbUtils.insert_wrapper(curs, "optionT", {"id": option_id, "content_object_id": (content_object_id := f"poll_candidate_list/{pcl_id}"), "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", (pcl_id,)) + assert pcl_id == DbUtils.insert_wrapper(curs, "poll_candidate_list_t", {"id": pcl_id, "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('option_t', 'id'), %s);", (option_id,)) + assert option_id == DbUtils.insert_wrapper(curs, "option_t", {"id": option_id, "content_object_id": (content_object_id := f"poll_candidate_list/{pcl_id}"), "meeting_id": self.meeting1_id}) option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_poll_candidate_list_id", "content_object_id_user_id"]) assert option_row["id"] == option_id assert option_row["content_object_id"] == content_object_id @@ -150,8 +150,8 @@ def test_generic_1GT_nt(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): option_id = 3 - curs.execute("select setval(pg_get_serial_sequence('poll_candidate_listT', 'id'), %s);", (option_id,)) - option_id == DbUtils.insert_wrapper(curs, "optionT", {"id": option_id, "content_object_id": (content_object_id := f"user/{self.user1_id}"), "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", (option_id,)) + option_id == DbUtils.insert_wrapper(curs, "option_t", {"id": option_id, "content_object_id": (content_object_id := f"user/{self.user1_id}"), "meeting_id": self.meeting1_id}) option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_user_id", "content_object_id_poll_candidate_list_id"]) assert option_row["id"] == option_id assert option_row["content_object_id"] == content_object_id @@ -167,10 +167,10 @@ def test_generic_1GTR_1t(self) -> None: with self.db_connection.transaction(): mediafile_id = 2 los_id = 3 - curs.execute("select setval(pg_get_serial_sequence('mediafileT', 'id'), %s);", (mediafile_id,)) - assert mediafile_id == DbUtils.insert_wrapper(curs, "mediafileT", {"id": mediafile_id, "is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}) - curs.execute("select setval(pg_get_serial_sequence('list_of_speakersT', 'id'), %s);", (los_id,)) - assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakersT", {"id": los_id, "content_object_id": (content_object_id := f"mediafile/{mediafile_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) + curs.execute("select setval(pg_get_serial_sequence('mediafile_t', 'id'), %s);", (mediafile_id,)) + assert mediafile_id == DbUtils.insert_wrapper(curs, "mediafile_t", {"id": mediafile_id, "is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}) + curs.execute("select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", (los_id,)) + assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakers_t", {"id": los_id, "content_object_id": (content_object_id := f"mediafile/{mediafile_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_mediafile_id", "content_object_id_topic_id"]) assert los_row["id"] == los_id assert los_row["content_object_id"] == content_object_id @@ -186,10 +186,10 @@ def test_generic_1GTR_1tR(self) -> None: with self.db_connection.transaction(): assignment_id = 2 los_id = 3 - curs.execute("select setval(pg_get_serial_sequence('assignmentT', 'id'), %s);", (assignment_id,)) - assert assignment_id == DbUtils.insert_wrapper(curs, "assignmentT", {"id": assignment_id, "title": "I am an assignment", "sequential_number": 42, "meeting_id": self.meeting1_id}) - curs.execute("select setval(pg_get_serial_sequence('list_of_speakersT', 'id'), %s);", (los_id,)) - assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakersT", {"id": los_id, "content_object_id": (content_object_id := f"assignment/{assignment_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) + curs.execute("select setval(pg_get_serial_sequence('assignment_t', 'id'), %s);", (assignment_id,)) + assert assignment_id == DbUtils.insert_wrapper(curs, "assignment_t", {"id": assignment_id, "title": "I am an assignment", "sequential_number": 42, "meeting_id": self.meeting1_id}) + curs.execute("select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", (los_id,)) + assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakers_t", {"id": los_id, "content_object_id": (content_object_id := f"assignment/{assignment_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_assignment_id", "content_object_id_topic_id"]) assert los_row["id"] == los_id assert los_row["content_object_id"] == content_object_id @@ -203,7 +203,7 @@ def test_generic_1GTR_1tR(self) -> None: def test_generic_1GTR_nt(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - DbUtils.insert_many_wrapper(curs, "mediafileT", [ + DbUtils.insert_many_wrapper(curs, "mediafile_t", [ { "is_public": True, "owner_id": f"meeting/{self.meeting1_id}" @@ -235,7 +235,7 @@ def test_generic_1Gt_check_constraint_error(self) -> None: with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - DbUtils.insert_wrapper(curs, "mediafileT", { + DbUtils.insert_wrapper(curs, "mediafile_t", { "is_public": True, "owner_id": f"motion_state/{self.meeting1_id}" }) @@ -245,7 +245,7 @@ def test_generic_1Gt_check_constraint_error(self) -> None: def test_generic_nGt_nt(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_ids = DbUtils.insert_many_wrapper(curs, "organization_tagT", [ + tag_ids = DbUtils.insert_many_wrapper(curs, "organization_tag_t", [ { "name": "Orga Tag 1", "color": 0xffee13 @@ -259,13 +259,13 @@ def test_generic_nGt_nt(self) -> None: "color": 0x00ee13 }, ]) - DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_idsT", [ + DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ {"organization_tag_id": tag_ids[0], "tagged_id": f"committee/{self.committee1_id}"}, {"organization_tag_id": tag_ids[0], "tagged_id": f"meeting/{self.meeting1_id}"}, {"organization_tag_id": tag_ids[1], "tagged_id": f"committee/{self.committee1_id}"}, {"organization_tag_id": tag_ids[2], "tagged_id": f"meeting/{self.meeting1_id}"}, ]) - rows = DbUtils.select_id_wrapper(curs, "gm_organization_tag_tagged_idsT", field_names=["id", "organization_tag_id", "tagged_id", "tagged_id_committee_id", "tagged_id_meeting_id"]) + rows = DbUtils.select_id_wrapper(curs, "gm_organization_tag_tagged_ids_t", field_names=["id", "organization_tag_id", "tagged_id", "tagged_id_committee_id", "tagged_id_meeting_id"]) expected_results = ((1, 1, "committee/1", 1, None), (2, 1, "meeting/1", None, 1), (3, 2, "committee/1", 1, None), (4, 3, "meeting/1", None, 1)) for i, row in enumerate (rows): assert tuple(row.values()) == expected_results[i] @@ -279,8 +279,8 @@ def test_generic_nGt_check_constraint_error(self) -> None: with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tagT", {"name": "Orga Tag 1", "color": 0xffee13}) - DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_idsT", [ + tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": 0xffee13}) + DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}, {"organization_tag_id": tag_id, "tagged_id": f"motion_state/{self.meeting1_id}"}, ]) @@ -289,9 +289,9 @@ def test_generic_nGt_check_constraint_error(self) -> None: def test_generic_nGt_unique_constraint_error(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tagT", {"name": "Orga Tag 1", "color": 0xffee13}) - DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_idsT", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) + tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": 0xffee13}) + DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.transaction(): - DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_idsT", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) + DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) assert 'duplicate key value violates unique constraint' in str(e) diff --git a/models.yml b/models.yml index 56df5149..fe719b7d 100644 --- a/models.yml +++ b/models.yml @@ -43,6 +43,9 @@ # reference: # - agenda_item # - assignment +# The `sql`-attribute can only be used for relation-types in combination with a `to`-attribute. The `required` attribute +# is not allowed, because in hand written sql we can't check for. +# Currently implemented only for n:m-relations without generating an intermediate table. # List of allowed and implemented relations # Symbols: # 1 = relation @@ -73,6 +76,7 @@ # ("nt", "nGt"): (FieldSqlErrorType.SQL, False), # ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), # ("ntR", "1r"): (FieldSqlErrorType.SQL, False), +# ("nts", "nts"): (FieldSqlErrorType.SQL, False), # # - on_delete: This fields determines what should happen with the foreign model if # this model gets deleted. Possible values are: @@ -84,9 +88,6 @@ # - You can add JSON Schema properties to the fields like `enum`, `description`, # `items`, `maxLength` and `minimum` # Additional properties: -# - The optional property `sql` may contain an SQL statement which is used to calculate the value of this field. This -# field does not exist with its own value in a table, but is always defined in a view. It can be used on all -# field types, not only relations. # - The property `read_only` describes a field that can not be changed by an action. # - The property `default` describes the default value that is used for new objects. # - The property `required` describes that this field can not be null or an empty @@ -197,7 +198,6 @@ organization: type: relation-list to: committee/organization_id restriction_mode: B - sql: (select array_agg(c.id) from committeeT c) as committee_ids reference: committee active_meeting_ids: type: relation-list @@ -357,20 +357,20 @@ user: # - is member (= has group-rights) of a meeting in the committee # TODO: fix sql, not correct for the current design and naming committee_ids: - type: number[] + type: relation-list to: committee/user_ids restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sql: >- - select array_agg(committee_id) from ( + sqlTODO: >- + (select array_agg(committee_id) from select m.committee_id as committee_id from group_to_user gtu join groupT g on g.id = gtu.group_id join meetingT m on m.id = g.meeting_id where gtu.user_id = u.id union select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id - ) as cs + ) as committee_ids # committee specific permissions committee_management_ids: @@ -414,6 +414,7 @@ user: type: number[] description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true + sql: todo restriction_mode: E organization_id: type: relation @@ -741,20 +742,20 @@ committee: reference: meeting restriction_mode: A user_ids: - type: number[] + type: relation-list to: user/committee_ids restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sql: >- - select array_agg(user_id) from ( + sqlTODO: >- + (select array_agg(user_id) from select gtu.user_id as user_id from meetingT m join groupT g on g.meeting_id = m.id join group_to_user gtu on gtu.group_id = g.id where m.committee_id = c.id union select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id - ) as x + ) as user_ids manager_ids: type: relation-list to: user/committee_management_ids From f8e5d545a4891c4c9f0ebc100a6f55233712bf69 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Tue, 9 Apr 2024 14:49:10 +0200 Subject: [PATCH 045/142] Fix close issues action --- .github/workflows/close-issues.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml index 98274a8d..cc5c4ec9 100644 --- a/.github/workflows/close-issues.yml +++ b/.github/workflows/close-issues.yml @@ -38,8 +38,8 @@ jobs: } } variables: | - owner: ${{ github.event.repository.owner.name }} - repo: ${{ github.event.repository.name }} + owner: ${{ github.repository_owner }} + name: ${{ github.event.repository.name }} number: ${{ github.event.pull_request.number }} - name: Close issues From 8a9d693316bdae035d9baae2b85a228ab625df86 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Tue, 9 Apr 2024 15:14:58 +0200 Subject: [PATCH 046/142] Fix close issues --- .github/workflows/close-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml index cc5c4ec9..5df599f4 100644 --- a/.github/workflows/close-issues.yml +++ b/.github/workflows/close-issues.yml @@ -46,7 +46,7 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | - issue_data="$(echo "${{ steps.get-issues.outputs.data }}" | jq -r '.data.repository.pullRequest.closingIssuesReferences.nodes[] | [.number,.repository.nameWithOwner] | @tsv')" + issue_data="$(echo '${{ steps.get-issues.outputs.data }}' | jq -r '.repository.pullRequest.closingIssuesReferences.nodes[] | [.number,.repository.nameWithOwner] | @tsv')" echo "$issue_data" | while read number nameWithOwner; do gh issue close "$number" -r "$nameWithOwner" done From 13b465389c22a2ae50efeec9b1958aeea5a2c681 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Tue, 9 Apr 2024 15:48:40 +0200 Subject: [PATCH 047/142] Fix close issues --- .github/workflows/close-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml index 5df599f4..fcb5e997 100644 --- a/.github/workflows/close-issues.yml +++ b/.github/workflows/close-issues.yml @@ -47,6 +47,6 @@ jobs: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | issue_data="$(echo '${{ steps.get-issues.outputs.data }}' | jq -r '.repository.pullRequest.closingIssuesReferences.nodes[] | [.number,.repository.nameWithOwner] | @tsv')" - echo "$issue_data" | while read number nameWithOwner; do + echo "$issue_data" | grep -v "^$" | while read number nameWithOwner; do gh issue close "$number" -r "$nameWithOwner" done From 6439e54987221d8292e17c37b9b02dfef0fc25a5 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 11 Apr 2024 08:56:27 +0200 Subject: [PATCH 048/142] enum checking in constraint instead pg enums (only string) --- dev/requirements.txt | 1 + dev/sql/schema_relational.sql | 464 +++------------------------- dev/src/db_utils.py | 4 + dev/src/generate_sql_schema.py | 60 ++-- dev/src/helper_get_names.py | 18 +- dev/tests/test_generic_relations.py | 56 ++++ models.yml | 2 +- 7 files changed, 138 insertions(+), 467 deletions(-) diff --git a/dev/requirements.txt b/dev/requirements.txt index c9c2a8f4..760f4541 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -10,6 +10,7 @@ simplejson==3.19.2 debugpy==1.8.0 requests==2.31.0 psycopg[binary]==3.1.18 # only dev, in production use psycopg +python-sql==1.4.3 # typing types-PyYAML==6.0.12.20240311 diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 54868289..1b335a06 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,386 +55,8 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'a5825cc3e8727870c460309635849354' +-- MODELS_YML_CHECKSUM = 'ac6ee8717b6cd0896409f80bfab30549' -- Type definitions -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_organization_default_language') THEN - CREATE TYPE enum_organization_default_language AS ENUM ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr'); - ELSE - RAISE NOTICE 'type "enum_organization_default_language" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_user_organization_management_level') THEN - CREATE TYPE enum_user_organization_management_level AS ENUM ('superadmin', 'can_manage_organization', 'can_manage_users'); - ELSE - RAISE NOTICE 'type "enum_user_organization_management_level" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_language') THEN - CREATE TYPE enum_meeting_language AS ENUM ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr'); - ELSE - RAISE NOTICE 'type "enum_meeting_language" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_applause_type') THEN - CREATE TYPE enum_meeting_applause_type AS ENUM ('applause-type-bar', 'applause-type-particles'); - ELSE - RAISE NOTICE 'type "enum_meeting_applause_type" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_csv_encoding') THEN - CREATE TYPE enum_meeting_export_csv_encoding AS ENUM ('utf-8', 'iso-8859-15'); - ELSE - RAISE NOTICE 'type "enum_meeting_export_csv_encoding" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_pagenumber_alignment') THEN - CREATE TYPE enum_meeting_export_pdf_pagenumber_alignment AS ENUM ('left', 'right', 'center'); - ELSE - RAISE NOTICE 'type "enum_meeting_export_pdf_pagenumber_alignment" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_fontsize') THEN - CREATE TYPE enum_meeting_export_pdf_fontsize AS ENUM ('10', '11', '12'); - ELSE - RAISE NOTICE 'type "enum_meeting_export_pdf_fontsize" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_export_pdf_pagesize') THEN - CREATE TYPE enum_meeting_export_pdf_pagesize AS ENUM ('A4', 'A5'); - ELSE - RAISE NOTICE 'type "enum_meeting_export_pdf_pagesize" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_numeral_system') THEN - CREATE TYPE enum_meeting_agenda_numeral_system AS ENUM ('arabic', 'roman'); - ELSE - RAISE NOTICE 'type "enum_meeting_agenda_numeral_system" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_item_creation') THEN - CREATE TYPE enum_meeting_agenda_item_creation AS ENUM ('always', 'never', 'default_yes', 'default_no'); - ELSE - RAISE NOTICE 'type "enum_meeting_agenda_item_creation" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_agenda_new_items_default_visibility') THEN - CREATE TYPE enum_meeting_agenda_new_items_default_visibility AS ENUM ('common', 'internal', 'hidden'); - ELSE - RAISE NOTICE 'type "enum_meeting_agenda_new_items_default_visibility" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_default_line_numbering') THEN - CREATE TYPE enum_meeting_motions_default_line_numbering AS ENUM ('outside', 'inline', 'none'); - ELSE - RAISE NOTICE 'type "enum_meeting_motions_default_line_numbering" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_recommendation_text_mode') THEN - CREATE TYPE enum_meeting_motions_recommendation_text_mode AS ENUM ('original', 'changed', 'diff', 'agreed'); - ELSE - RAISE NOTICE 'type "enum_meeting_motions_recommendation_text_mode" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_default_sorting') THEN - CREATE TYPE enum_meeting_motions_default_sorting AS ENUM ('number', 'weight'); - ELSE - RAISE NOTICE 'type "enum_meeting_motions_default_sorting" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_number_type') THEN - CREATE TYPE enum_meeting_motions_number_type AS ENUM ('per_category', 'serially_numbered', 'manually'); - ELSE - RAISE NOTICE 'type "enum_meeting_motions_number_type" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motions_amendments_text_mode') THEN - CREATE TYPE enum_meeting_motions_amendments_text_mode AS ENUM ('freestyle', 'fulltext', 'paragraph'); - ELSE - RAISE NOTICE 'type "enum_meeting_motions_amendments_text_mode" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_ballot_paper_selection') THEN - CREATE TYPE enum_meeting_motion_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); - ELSE - RAISE NOTICE 'type "enum_meeting_motion_poll_ballot_paper_selection" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_default_onehundred_percent_base') THEN - CREATE TYPE enum_meeting_motion_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); - ELSE - RAISE NOTICE 'type "enum_meeting_motion_poll_default_onehundred_percent_base" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_motion_poll_default_backend') THEN - CREATE TYPE enum_meeting_motion_poll_default_backend AS ENUM ('long', 'fast'); - ELSE - RAISE NOTICE 'type "enum_meeting_motion_poll_default_backend" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_users_pdf_wlan_encryption') THEN - CREATE TYPE enum_meeting_users_pdf_wlan_encryption AS ENUM ('', 'WEP', 'WPA', 'nopass'); - ELSE - RAISE NOTICE 'type "enum_meeting_users_pdf_wlan_encryption" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_ballot_paper_selection') THEN - CREATE TYPE enum_meeting_assignment_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); - ELSE - RAISE NOTICE 'type "enum_meeting_assignment_poll_ballot_paper_selection" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_default_onehundred_percent_base') THEN - CREATE TYPE enum_meeting_assignment_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); - ELSE - RAISE NOTICE 'type "enum_meeting_assignment_poll_default_onehundred_percent_base" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_assignment_poll_default_backend') THEN - CREATE TYPE enum_meeting_assignment_poll_default_backend AS ENUM ('long', 'fast'); - ELSE - RAISE NOTICE 'type "enum_meeting_assignment_poll_default_backend" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_ballot_paper_selection') THEN - CREATE TYPE enum_meeting_poll_ballot_paper_selection AS ENUM ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER'); - ELSE - RAISE NOTICE 'type "enum_meeting_poll_ballot_paper_selection" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_default_onehundred_percent_base') THEN - CREATE TYPE enum_meeting_poll_default_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); - ELSE - RAISE NOTICE 'type "enum_meeting_poll_default_onehundred_percent_base" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_meeting_poll_default_backend') THEN - CREATE TYPE enum_meeting_poll_default_backend AS ENUM ('long', 'fast'); - ELSE - RAISE NOTICE 'type "enum_meeting_poll_default_backend" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_group_permissions') THEN - CREATE TYPE enum_group_permissions AS ENUM ('agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update'); - ELSE - RAISE NOTICE 'type "enum_group_permissions" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_agenda_item_type') THEN - CREATE TYPE enum_agenda_item_type AS ENUM ('common', 'internal', 'hidden'); - ELSE - RAISE NOTICE 'type "enum_agenda_item_type" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_speaker_speech_state') THEN - CREATE TYPE enum_speaker_speech_state AS ENUM ('contribution', 'pro', 'contra', 'intervention', 'interposed_question'); - ELSE - RAISE NOTICE 'type "enum_speaker_speech_state" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_change_recommendation_type') THEN - CREATE TYPE enum_motion_change_recommendation_type AS ENUM ('replacement', 'insertion', 'deletion', 'other'); - ELSE - RAISE NOTICE 'type "enum_motion_change_recommendation_type" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_css_class') THEN - CREATE TYPE enum_motion_state_css_class AS ENUM ('grey', 'red', 'green', 'lightblue', 'yellow'); - ELSE - RAISE NOTICE 'type "enum_motion_state_css_class" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_restrictions') THEN - CREATE TYPE enum_motion_state_restrictions AS ENUM ('motion.can_see_internal', 'motion.can_manage_metadata', 'motion.can_manage', 'is_submitter'); - ELSE - RAISE NOTICE 'type "enum_motion_state_restrictions" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_motion_state_merge_amendment_into_final') THEN - CREATE TYPE enum_motion_state_merge_amendment_into_final AS ENUM ('do_not_merge', 'undefined', 'do_merge'); - ELSE - RAISE NOTICE 'type "enum_motion_state_merge_amendment_into_final" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_type') THEN - CREATE TYPE enum_poll_type AS ENUM ('analog', 'named', 'pseudoanonymous', 'cryptographic'); - ELSE - RAISE NOTICE 'type "enum_poll_type" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_backend') THEN - CREATE TYPE enum_poll_backend AS ENUM ('long', 'fast'); - ELSE - RAISE NOTICE 'type "enum_poll_backend" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_pollmethod') THEN - CREATE TYPE enum_poll_pollmethod AS ENUM ('Y', 'YN', 'YNA', 'N'); - ELSE - RAISE NOTICE 'type "enum_poll_pollmethod" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_state') THEN - CREATE TYPE enum_poll_state AS ENUM ('created', 'started', 'finished', 'published'); - ELSE - RAISE NOTICE 'type "enum_poll_state" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_poll_onehundred_percent_base') THEN - CREATE TYPE enum_poll_onehundred_percent_base AS ENUM ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled'); - ELSE - RAISE NOTICE 'type "enum_poll_onehundred_percent_base" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_assignment_phase') THEN - CREATE TYPE enum_assignment_phase AS ENUM ('search', 'voting', 'finished'); - ELSE - RAISE NOTICE 'type "enum_assignment_phase" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_action_worker_state') THEN - CREATE TYPE enum_action_worker_state AS ENUM ('running', 'end', 'aborted'); - ELSE - RAISE NOTICE 'type "enum_action_worker_state" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_import_preview_name') THEN - CREATE TYPE enum_import_preview_name AS ENUM ('account', 'participant', 'topic', 'committee', 'motion'); - ELSE - RAISE NOTICE 'type "enum_import_preview_name" already exists, skipping'; - END IF; -END$$; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'enum_import_preview_state') THEN - CREATE TYPE enum_import_preview_state AS ENUM ('warning', 'error', 'done'); - ELSE - RAISE NOTICE 'type "enum_import_preview_state" already exists, skipping'; - END IF; -END$$; - -- Table definitions CREATE TABLE IF NOT EXISTS organization_t ( @@ -450,7 +72,7 @@ CREATE TABLE IF NOT EXISTS organization_t ( enable_chat boolean, limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, - default_language enum_organization_default_language NOT NULL, + default_language varchar(256) NOT NULL CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), saml_enabled boolean, saml_login_button_text varchar(256) DEFAULT 'SAML login', saml_attr_mapping jsonb, @@ -505,7 +127,7 @@ CREATE TABLE IF NOT EXISTS user_t ( last_email_sent timestamptz, is_demo_user boolean, last_login timestamptz, - organization_management_level enum_user_organization_management_level, + organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -624,7 +246,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( start_time timestamptz, end_time timestamptz, imported_at timestamptz, - language enum_meeting_language NOT NULL, + language varchar(256) NOT NULL CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) CONSTRAINT minlength_language CHECK (char_length(language) >= 1), jitsi_domain varchar(256), jitsi_room_name varchar(256), jitsi_room_password varchar(256), @@ -641,7 +263,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( conference_auto_connect_next_speakers integer DEFAULT 0, conference_enable_helpdesk boolean DEFAULT False, applause_enable boolean DEFAULT False, - applause_type enum_meeting_applause_type DEFAULT 'applause-type-bar', + applause_type varchar(256) CONSTRAINT enum_meeting_applause_type CHECK (applause_type IN ('applause-type-bar', 'applause-type-particles')) DEFAULT 'applause-type-bar', applause_show_level boolean DEFAULT False, applause_min_amount integer CONSTRAINT minimum_applause_min_amount CHECK (applause_min_amount >= 0) DEFAULT 1, applause_max_amount integer CONSTRAINT minimum_applause_max_amount CHECK (applause_max_amount >= 0) DEFAULT 0, @@ -649,22 +271,22 @@ CREATE TABLE IF NOT EXISTS meeting_t ( applause_particle_image_url varchar(256), projector_countdown_default_time integer NOT NULL DEFAULT 60, projector_countdown_warning_time integer NOT NULL CONSTRAINT minimum_projector_countdown_warning_time CHECK (projector_countdown_warning_time >= 0) DEFAULT 0, - export_csv_encoding enum_meeting_export_csv_encoding DEFAULT 'utf-8', + export_csv_encoding varchar(256) CONSTRAINT enum_meeting_export_csv_encoding CHECK (export_csv_encoding IN ('utf-8', 'iso-8859-15')) DEFAULT 'utf-8', export_csv_separator varchar(256) DEFAULT ';', - export_pdf_pagenumber_alignment enum_meeting_export_pdf_pagenumber_alignment DEFAULT 'center', - export_pdf_fontsize enum_meeting_export_pdf_fontsize DEFAULT '10', + export_pdf_pagenumber_alignment varchar(256) CONSTRAINT enum_meeting_export_pdf_pagenumber_alignment CHECK (export_pdf_pagenumber_alignment IN ('left', 'right', 'center')) DEFAULT 'center', + export_pdf_fontsize integer CONSTRAINT enum_meeting_export_pdf_fontsize CHECK (export_pdf_fontsize IN (10, 11, 12)) DEFAULT 10, export_pdf_line_height real CONSTRAINT minimum_export_pdf_line_height CHECK (export_pdf_line_height >= 1.0) DEFAULT 1.25, export_pdf_page_margin_left integer CONSTRAINT minimum_export_pdf_page_margin_left CHECK (export_pdf_page_margin_left >= 0) DEFAULT 20, export_pdf_page_margin_top integer CONSTRAINT minimum_export_pdf_page_margin_top CHECK (export_pdf_page_margin_top >= 0) DEFAULT 25, export_pdf_page_margin_right integer CONSTRAINT minimum_export_pdf_page_margin_right CHECK (export_pdf_page_margin_right >= 0) DEFAULT 20, export_pdf_page_margin_bottom integer CONSTRAINT minimum_export_pdf_page_margin_bottom CHECK (export_pdf_page_margin_bottom >= 0) DEFAULT 20, - export_pdf_pagesize enum_meeting_export_pdf_pagesize DEFAULT 'A4', + export_pdf_pagesize varchar(256) CONSTRAINT enum_meeting_export_pdf_pagesize CHECK (export_pdf_pagesize IN ('A4', 'A5')) DEFAULT 'A4', agenda_show_subtitles boolean DEFAULT False, agenda_enable_numbering boolean DEFAULT True, agenda_number_prefix varchar(20), - agenda_numeral_system enum_meeting_agenda_numeral_system DEFAULT 'arabic', - agenda_item_creation enum_meeting_agenda_item_creation DEFAULT 'default_no', - agenda_new_items_default_visibility enum_meeting_agenda_new_items_default_visibility DEFAULT 'internal', + agenda_numeral_system varchar(256) CONSTRAINT enum_meeting_agenda_numeral_system CHECK (agenda_numeral_system IN ('arabic', 'roman')) DEFAULT 'arabic', + agenda_item_creation varchar(256) CONSTRAINT enum_meeting_agenda_item_creation CHECK (agenda_item_creation IN ('always', 'never', 'default_yes', 'default_no')) DEFAULT 'default_no', + agenda_new_items_default_visibility varchar(256) CONSTRAINT enum_meeting_agenda_new_items_default_visibility CHECK (agenda_new_items_default_visibility IN ('common', 'internal', 'hidden')) DEFAULT 'internal', agenda_show_internal_items_on_projector boolean DEFAULT False, list_of_speakers_amount_last_on_projector integer CONSTRAINT minimum_list_of_speakers_amount_last_on_projector CHECK (list_of_speakers_amount_last_on_projector >= -1) DEFAULT 0, list_of_speakers_amount_next_on_projector integer CONSTRAINT minimum_list_of_speakers_amount_next_on_projector CHECK (list_of_speakers_amount_next_on_projector >= -1) DEFAULT -1, @@ -689,7 +311,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( motions_default_amendment_workflow_id integer NOT NULL, motions_default_statute_amendment_workflow_id integer NOT NULL, motions_preamble text DEFAULT 'The assembly may decide:', - motions_default_line_numbering enum_meeting_motions_default_line_numbering DEFAULT 'outside', + motions_default_line_numbering varchar(256) CONSTRAINT enum_meeting_motions_default_line_numbering CHECK (motions_default_line_numbering IN ('outside', 'inline', 'none')) DEFAULT 'outside', motions_line_length integer CONSTRAINT minimum_motions_line_length CHECK (motions_line_length >= 40) DEFAULT 85, motions_reason_required boolean DEFAULT False, motions_enable_text_on_projector boolean DEFAULT True, @@ -701,9 +323,9 @@ CREATE TABLE IF NOT EXISTS meeting_t ( motions_recommendations_by varchar(256), motions_block_slide_columns integer CONSTRAINT minimum_motions_block_slide_columns CHECK (motions_block_slide_columns >= 1), motions_statute_recommendations_by varchar(256), - motions_recommendation_text_mode enum_meeting_motions_recommendation_text_mode DEFAULT 'diff', - motions_default_sorting enum_meeting_motions_default_sorting DEFAULT 'number', - motions_number_type enum_meeting_motions_number_type DEFAULT 'per_category', + motions_recommendation_text_mode varchar(256) CONSTRAINT enum_meeting_motions_recommendation_text_mode CHECK (motions_recommendation_text_mode IN ('original', 'changed', 'diff', 'agreed')) DEFAULT 'diff', + motions_default_sorting varchar(256) CONSTRAINT enum_meeting_motions_default_sorting CHECK (motions_default_sorting IN ('number', 'weight')) DEFAULT 'number', + motions_number_type varchar(256) CONSTRAINT enum_meeting_motions_number_type CHECK (motions_number_type IN ('per_category', 'serially_numbered', 'manually')) DEFAULT 'per_category', motions_number_min_digits integer DEFAULT 2, motions_number_with_blank boolean DEFAULT False, motions_statutes_enabled boolean DEFAULT False, @@ -711,7 +333,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( motions_amendments_in_main_list boolean DEFAULT True, motions_amendments_of_amendments boolean DEFAULT False, motions_amendments_prefix varchar(256) DEFAULT '-Ä', - motions_amendments_text_mode enum_meeting_motions_amendments_text_mode DEFAULT 'paragraph', + motions_amendments_text_mode varchar(256) CONSTRAINT enum_meeting_motions_amendments_text_mode CHECK (motions_amendments_text_mode IN ('freestyle', 'fulltext', 'paragraph')) DEFAULT 'paragraph', motions_amendments_multiple_paragraphs boolean DEFAULT True, motions_supporters_min_amount integer CONSTRAINT minimum_motions_supporters_min_amount CHECK (motions_supporters_min_amount >= 0) DEFAULT 0, motions_enable_editor boolean, @@ -720,11 +342,11 @@ CREATE TABLE IF NOT EXISTS meeting_t ( motions_export_preamble text, motions_export_submitter_recommendation boolean DEFAULT True, motions_export_follow_recommendation boolean DEFAULT False, - motion_poll_ballot_paper_selection enum_meeting_motion_poll_ballot_paper_selection DEFAULT 'CUSTOM_NUMBER', + motion_poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_motion_poll_ballot_paper_selection CHECK (motion_poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')) DEFAULT 'CUSTOM_NUMBER', motion_poll_ballot_paper_number integer DEFAULT 8, motion_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', - motion_poll_default_onehundred_percent_base enum_meeting_motion_poll_default_onehundred_percent_base DEFAULT 'YNA', - motion_poll_default_backend enum_meeting_motion_poll_default_backend DEFAULT 'fast', + motion_poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_motion_poll_default_onehundred_percent_base CHECK (motion_poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', + motion_poll_default_backend varchar(256) CONSTRAINT enum_meeting_motion_poll_default_backend CHECK (motion_poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', users_enable_presence_view boolean DEFAULT False, users_enable_vote_weight boolean DEFAULT False, users_allow_self_set_present boolean DEFAULT True, @@ -732,7 +354,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( users_pdf_welcometext text DEFAULT '[Place for your welcome and help text.]', users_pdf_wlan_ssid varchar(256), users_pdf_wlan_password varchar(256), - users_pdf_wlan_encryption enum_meeting_users_pdf_wlan_encryption DEFAULT 'WPA', + users_pdf_wlan_encryption varchar(256) CONSTRAINT enum_meeting_users_pdf_wlan_encryption CHECK (users_pdf_wlan_encryption IN ('', 'WEP', 'WPA', 'nopass')) DEFAULT 'WPA', users_email_sender varchar(256) DEFAULT 'OpenSlides', users_email_replyto varchar(256), users_email_subject varchar(256) DEFAULT 'OpenSlides access data', @@ -749,22 +371,22 @@ This email was generated automatically.', users_enable_vote_delegations boolean, assignments_export_title varchar(256) DEFAULT 'Elections', assignments_export_preamble text, - assignment_poll_ballot_paper_selection enum_meeting_assignment_poll_ballot_paper_selection DEFAULT 'CUSTOM_NUMBER', + assignment_poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_assignment_poll_ballot_paper_selection CHECK (assignment_poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')) DEFAULT 'CUSTOM_NUMBER', assignment_poll_ballot_paper_number integer DEFAULT 8, assignment_poll_add_candidates_to_list_of_speakers boolean DEFAULT False, assignment_poll_enable_max_votes_per_option boolean DEFAULT False, assignment_poll_sort_poll_result_by_votes boolean DEFAULT True, assignment_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', assignment_poll_default_method varchar(256) DEFAULT 'Y', - assignment_poll_default_onehundred_percent_base enum_meeting_assignment_poll_default_onehundred_percent_base DEFAULT 'valid', - assignment_poll_default_backend enum_meeting_assignment_poll_default_backend DEFAULT 'fast', - poll_ballot_paper_selection enum_meeting_poll_ballot_paper_selection, + assignment_poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_assignment_poll_default_onehundred_percent_base CHECK (assignment_poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'valid', + assignment_poll_default_backend varchar(256) CONSTRAINT enum_meeting_assignment_poll_default_backend CHECK (assignment_poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', + poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_poll_ballot_paper_selection CHECK (poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')), poll_ballot_paper_number integer, poll_sort_poll_result_by_votes boolean, poll_default_type varchar(256) DEFAULT 'analog', poll_default_method varchar(256), - poll_default_onehundred_percent_base enum_meeting_poll_default_onehundred_percent_base DEFAULT 'YNA', - poll_default_backend enum_meeting_poll_default_backend DEFAULT 'fast', + poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', + poll_default_backend varchar(256) CONSTRAINT enum_meeting_poll_default_backend CHECK (poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', poll_couple_countdown boolean DEFAULT True, logo_projector_main_id integer, logo_projector_header_id integer, @@ -816,7 +438,7 @@ CREATE TABLE IF NOT EXISTS group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), name varchar(256) NOT NULL, - permissions enum_group_permissions[], + permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update']::varchar[]), weight integer, used_as_motion_poll_default_id integer, used_as_assignment_poll_default_id integer, @@ -858,7 +480,7 @@ CREATE TABLE IF NOT EXISTS agenda_item_t ( item_number varchar(256), comment varchar(256), closed boolean DEFAULT False, - type enum_agenda_item_type DEFAULT 'common', + type varchar(256) CONSTRAINT enum_agenda_item_type CHECK (type IN ('common', 'internal', 'hidden')) DEFAULT 'common', duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), moderator_notes text, is_internal boolean, @@ -939,7 +561,7 @@ CREATE TABLE IF NOT EXISTS speaker_t ( unpause_time timestamptz, total_pause integer, weight integer DEFAULT 10000, - speech_state enum_speaker_speech_state, + speech_state varchar(256) CONSTRAINT enum_speaker_speech_state CHECK (speech_state IN ('contribution', 'pro', 'contra', 'intervention', 'interposed_question')), note varchar(250), point_of_order boolean, list_of_speakers_id integer NOT NULL, @@ -1098,7 +720,7 @@ CREATE TABLE IF NOT EXISTS motion_change_recommendation_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, rejected boolean DEFAULT False, internal boolean DEFAULT False, - type enum_motion_change_recommendation_type DEFAULT 'replacement', + type varchar(256) CONSTRAINT enum_motion_change_recommendation_type CHECK (type IN ('replacement', 'insertion', 'deletion', 'other')) DEFAULT 'replacement', other_description varchar(256), line_from integer CONSTRAINT minimum_line_from CHECK (line_from >= 0), line_to integer CONSTRAINT minimum_line_to CHECK (line_to >= 0), @@ -1117,15 +739,15 @@ CREATE TABLE IF NOT EXISTS motion_state_t ( weight integer NOT NULL, recommendation_label varchar(256), is_internal boolean, - css_class enum_motion_state_css_class NOT NULL DEFAULT 'lightblue', - restrictions enum_motion_state_restrictions[] DEFAULT '{}', + css_class varchar(256) NOT NULL CONSTRAINT enum_motion_state_css_class CHECK (css_class IN ('grey', 'red', 'green', 'lightblue', 'yellow')) DEFAULT 'lightblue', + restrictions varchar(256)[] CONSTRAINT enum_motion_state_restrictions CHECK (restrictions <@ ARRAY['motion.can_see_internal', 'motion.can_manage_metadata', 'motion.can_manage', 'is_submitter']::varchar[]) DEFAULT '{}', allow_support boolean DEFAULT False, allow_create_poll boolean DEFAULT False, allow_submitter_edit boolean DEFAULT False, set_number boolean DEFAULT True, show_state_extension_field boolean DEFAULT False, show_recommendation_extension_field boolean DEFAULT False, - merge_amendment_into_final enum_motion_state_merge_amendment_into_final DEFAULT 'undefined', + merge_amendment_into_final varchar(256) CONSTRAINT enum_motion_state_merge_amendment_into_final CHECK (merge_amendment_into_final IN ('do_not_merge', 'undefined', 'do_merge')) DEFAULT 'undefined', allow_motion_forwarding boolean DEFAULT False, set_workflow_timestamp boolean DEFAULT False, submitter_withdraw_state_id integer, @@ -1167,18 +789,18 @@ CREATE TABLE IF NOT EXISTS poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, description text, title varchar(256) NOT NULL, - type enum_poll_type NOT NULL, - backend enum_poll_backend NOT NULL DEFAULT 'fast', + type varchar(256) NOT NULL CONSTRAINT enum_poll_type CHECK (type IN ('analog', 'named', 'pseudoanonymous', 'cryptographic')), + backend varchar(256) NOT NULL CONSTRAINT enum_poll_backend CHECK (backend IN ('long', 'fast')) DEFAULT 'fast', is_pseudoanonymized boolean, - pollmethod enum_poll_pollmethod NOT NULL, - state enum_poll_state DEFAULT 'created', + pollmethod varchar(256) NOT NULL CONSTRAINT enum_poll_pollmethod CHECK (pollmethod IN ('Y', 'YN', 'YNA', 'N')), + state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', min_votes_amount integer CONSTRAINT minimum_min_votes_amount CHECK (min_votes_amount >= 1) DEFAULT 1, max_votes_amount integer CONSTRAINT minimum_max_votes_amount CHECK (max_votes_amount >= 1) DEFAULT 1, max_votes_per_option integer CONSTRAINT minimum_max_votes_per_option CHECK (max_votes_per_option >= 1) DEFAULT 1, global_yes boolean DEFAULT False, global_no boolean DEFAULT False, global_abstain boolean DEFAULT False, - onehundred_percent_base enum_poll_onehundred_percent_base NOT NULL DEFAULT 'disabled', + onehundred_percent_base varchar(256) NOT NULL CONSTRAINT enum_poll_onehundred_percent_base CHECK (onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'disabled', votesvalid decimal(6), votesinvalid decimal(6), votescast decimal(6), @@ -1250,7 +872,7 @@ CREATE TABLE IF NOT EXISTS assignment_t ( title varchar(256) NOT NULL, description text, open_posts integer CONSTRAINT minimum_open_posts CHECK (open_posts >= 0) DEFAULT 0, - phase enum_assignment_phase DEFAULT 'search', + phase varchar(256) CONSTRAINT enum_assignment_phase CHECK (phase IN ('search', 'voting', 'finished')) DEFAULT 'search', default_poll_description text, number_poll_candidates boolean, sequential_number integer NOT NULL, @@ -1442,7 +1064,7 @@ CREATE TABLE IF NOT EXISTS chat_message_t ( CREATE TABLE IF NOT EXISTS action_worker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, - state enum_action_worker_state NOT NULL, + state varchar(256) NOT NULL CONSTRAINT enum_action_worker_state CHECK (state IN ('running', 'end', 'aborted')), created timestamptz NOT NULL, timestamp timestamptz NOT NULL, result jsonb @@ -1453,8 +1075,8 @@ CREATE TABLE IF NOT EXISTS action_worker_t ( CREATE TABLE IF NOT EXISTS import_preview_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, - name enum_import_preview_name NOT NULL, - state enum_import_preview_state NOT NULL, + name varchar(256) NOT NULL CONSTRAINT enum_import_preview_name CHECK (name IN ('account', 'participant', 'topic', 'committee', 'motion')), + state varchar(256) NOT NULL CONSTRAINT enum_import_preview_state CHECK (state IN ('warning', 'error', 'done')), created timestamptz NOT NULL, result jsonb ); diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index d3dbbb16..46bc08bd 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -4,6 +4,10 @@ class DbUtils: + @classmethod + def get_pg_array_for_cu(cls, data: list) -> str: + return f"""{{"{'","'.join(item for item in data)}"}}""" + @classmethod def insert_wrapper( cls, curs: Cursor, table_name: str, data: dict[str, Any] diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 8ed8d80a..851d7744 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -66,8 +66,8 @@ class SubstDict(TypedDict, total=False): default: str minimum: str minLength: str - enum_: str deferred: str + check_enum: str class GenerateCodeBlocks: @@ -144,12 +144,6 @@ def generate_the_code( schema_zone_texts["undecided"] += error errors.append(error) else: - if (enum_ := fdata.get("enum")) or ( - enum_ := fdata.get("items", {}).get("enum") - ): - pre_code += Helper.get_enum_type_definition( - table_name, fname, enum_ - ) result, error = method_or_str(table_name, fname, fdata, type_) for k, v in result.items(): schema_zone_texts[k] += v or "" # type: ignore @@ -641,21 +635,7 @@ class Helper: """ ) FIELD_TEMPLATE = string.Template( - " ${field_name} ${type}${primary_key}${required}${minimum}${minLength}${default},\n" - ) - ENUM_DEFINITION_TEMPLATE = string.Template( - dedent( - """ - DO $$$$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${enum_type}') THEN - CREATE TYPE ${enum_type} AS ENUM (${enumeration}); - ELSE - RAISE NOTICE 'type "${enum_type}" already exists, skipping'; - END IF; - END$$$$; - """ - ) + " ${field_name} ${type}${primary_key}${required}${check_enum}${minimum}${minLength}${default},\n" ) INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE = string.Template( dedent( @@ -758,14 +738,23 @@ def get_undecided_all(table_name: str, code: str) -> str: ) @staticmethod - def get_enum_type_definition(table_name: str, fname: str, enum_: list[Any]) -> str: - # enums per type are always strings in postgres - enumeration = ", ".join([f"'{str(item)}'" for item in enum_]) - subst = { - "enum_type": HelperGetNames.get_enum_type_name(fname, table_name), - "enumeration": enumeration, - } - return Helper.ENUM_DEFINITION_TEMPLATE.substitute(subst) + def get_check_enum( + table_name: str, fname: str, enum_: list[Any], type_: str + ) -> str: + check_enum_constraint_name = HelperGetNames.get_check_enum_constraint_name( + table_name, fname + ) + if type_.startswith("number"): + enumeration = ", ".join([str(item) for item in enum_]) + elif type_.startswith("string"): + enumeration = ", ".join([f"'{item}'" for item in enum_]) + else: + raise Exception(f"enum for type {type_} not implemented") + if type_.endswith("[]"): + condition = f"{fname} <@ ARRAY[{enumeration}]::varchar[]" + else: + condition = f"{fname} IN ({enumeration})" + return f" CONSTRAINT {check_enum_constraint_name} CHECK ({condition})" @staticmethod def get_foreign_key_table_constraint_as_alter_table( @@ -901,12 +890,7 @@ def get_initials( for form in Formatter().parse(Helper.FIELD_TEMPLATE.template) ] subst: SubstDict = cast(SubstDict, {k: "" for k in flist}) - if (enum_ := fdata.get("enum")) or fdata.get("items"): - subst_type = HelperGetNames.get_enum_type_name(fname, table_name) - if not enum_: - subst_type += "[]" - else: - subst_type = FIELD_TYPES[type_]["pg_type"] + subst_type = FIELD_TYPES[type_]["pg_type"] subst.update({"field_name": fname, "type": subst_type}) if fdata.get("required"): subst["required"] = " NOT NULL" @@ -922,6 +906,10 @@ def get_initials( raise Exception( f"{table_name}.{fname}: seems to be an invalid default value" ) + if (enum_ := fdata.get("enum")) or ( + enum_ := fdata.get("items", {}).get("enum") + ): + subst["check_enum"] = Helper.get_check_enum(table_name, fname, enum_, type_) if (minimum := fdata.get("minimum")) is not None: minimum_constraint_name = HelperGetNames.get_minimum_constraint_name(fname) subst["minimum"] = ( diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index b1faa1a8..dbc94364 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -112,15 +112,6 @@ def get_gm_content_field(table: str, field: str) -> str: """Gets the name of content field in an generic:many intermediate table""" return f"{table}_{field}_id" - @staticmethod - @max_length - def get_enum_type_name( - fname: str, - table_name: str, - ) -> str: - """gets the name of an enum with prefix enum, table_name_name and fname""" - return f"enum_{table_name}_{fname}" - @staticmethod @max_length def get_generic_valid_constraint_name( @@ -141,6 +132,15 @@ def get_generic_unique_constraint_name( """ return f"unique_${own_table_name_with_ref_column}_${own_table_column}" + @staticmethod + @max_length + def get_check_enum_constraint_name( + table_name: str, + fname: str, + ) -> str: + """gets the name of check enum constraint""" + return f"enum_{table_name}_{fname}" + @staticmethod @max_length def get_minimum_constraint_name( diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 25247f31..728a6458 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -4,6 +4,9 @@ import psycopg import pytest from psycopg import sql +from sql import Table +from sql.aggregate import * +from sql.conditionals import * from src.db_utils import DbUtils from tests.base import BaseTestCase @@ -295,3 +298,56 @@ def test_generic_nGt_unique_constraint_error(self) -> None: with self.db_connection.transaction(): DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) assert 'duplicate key value violates unique constraint' in str(e) + +class EnumTests(BaseTestCase): + def test_correct_singular_values_in_meeting(self) -> None: + meeting_t = Table("meeting_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + meeting = curs.execute(*meeting_t.select(meeting_t.language, meeting_t.export_pdf_fontsize, where=meeting_t.id==1)).fetchone() + assert meeting["language"] == "en" + assert meeting["export_pdf_fontsize"] == 10 + meeting = curs.execute(*meeting_t.update([meeting_t.language, meeting_t.export_pdf_fontsize], ["de", 11], where=meeting_t.id==1, returning=[meeting_t.id, meeting_t.language])).fetchone() + assert meeting["language"] == "de" + + def test_wrong_language_in_meeting(self) -> None: + meeting_t = Table("meeting_t") + with self.db_connection.cursor() as curs: + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.transaction(): + curs.execute(*meeting_t.update([meeting_t.language], ["xx"], where=meeting_t.id==1)) + assert 'violates check constraint "enum_meeting_language"' in str(e) + + def test_wrong_pdf_fontsize_in_meeting(self) -> None: + meeting_t = Table("meeting_t") + with self.db_connection.cursor() as curs: + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.transaction(): + curs.execute(*meeting_t.update([meeting_t.export_pdf_fontsize], [22], where=meeting_t.id==1)) + assert 'violates check constraint "enum_meeting_export_pdf_fontsize"' in str(e) + + def test_correct_permissions_in_group(self) -> None: + group_t = Table("group_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + group = curs.execute(*group_t.select(group_t.permissions, where=group_t.id==1)).fetchone() + assert "agenda_item.can_see_internal" in group["permissions"] + assert "user.can_see" in group["permissions"] + assert "chat.can_manage" not in group["permissions"] + group["permissions"].remove("user.can_see") + group["permissions"].append("chat.can_manage") + sql = tuple(group_t.update([group_t.permissions], [DbUtils.get_pg_array_for_cu(group["permissions"]),], where=group_t.id==1, returning=[group_t.permissions])) + group = curs.execute(*sql).fetchone() + assert "agenda_item.can_see_internal" in group["permissions"] + assert "user.can_see" not in group["permissions"] + assert "chat.can_manage" in group["permissions"] + + def test_wrong_permissions_in_group(self) -> None: + group_t = Table("group_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + with pytest.raises(psycopg.DatabaseError) as e: + group = {"permissions": ["user.can_see", "invalid permission"]} + sql = tuple(group_t.update([group_t.permissions], [DbUtils.get_pg_array_for_cu(group["permissions"]),], where=group_t.id==1, returning=[group_t.permissions])) + group = curs.execute(*sql).fetchone() + assert 'violates check constraint "enum_group_permissions"' in str(e) diff --git a/models.yml b/models.yml index fe719b7d..1607a424 100644 --- a/models.yml +++ b/models.yml @@ -982,7 +982,7 @@ meeting: default: center restriction_mode: B export_pdf_fontsize: - type: string + type: number enum: - 10 - 11 From e4641fbc1241519e137c3eb4bc86f5d49cfc19a6 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 12 Apr 2024 09:58:10 +0200 Subject: [PATCH 049/142] convert enums to constraints and colors to strings --- dev/sql/schema_relational.sql | 114 ++++++++++++++-------------- dev/src/generate_sql_schema.py | 10 +-- dev/tests/base.py | 34 ++++----- dev/tests/test_generic_relations.py | 59 ++++++++++++-- 4 files changed, 132 insertions(+), 85 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 1b335a06..766b941b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = 'ac6ee8717b6cd0896409f80bfab30549' +-- MODELS_YML_CHECKSUM = '959d3a581a8015a294d769587ebb1b6e' -- Type definitions -- Table definitions @@ -156,7 +156,7 @@ CREATE TABLE IF NOT EXISTS meeting_user_t ( CREATE TABLE IF NOT EXISTS organization_tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, - color integer CHECK (color >= 0 and color <= 16777215) NOT NULL, + color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') NOT NULL, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -166,52 +166,52 @@ CREATE TABLE IF NOT EXISTS organization_tag_t ( CREATE TABLE IF NOT EXISTS theme_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - accent_100 integer CHECK (accent_100 >= 0 and accent_100 <= 16777215), - accent_200 integer CHECK (accent_200 >= 0 and accent_200 <= 16777215), - accent_300 integer CHECK (accent_300 >= 0 and accent_300 <= 16777215), - accent_400 integer CHECK (accent_400 >= 0 and accent_400 <= 16777215), - accent_50 integer CHECK (accent_50 >= 0 and accent_50 <= 16777215), - accent_500 integer CHECK (accent_500 >= 0 and accent_500 <= 16777215) NOT NULL, - accent_600 integer CHECK (accent_600 >= 0 and accent_600 <= 16777215), - accent_700 integer CHECK (accent_700 >= 0 and accent_700 <= 16777215), - accent_800 integer CHECK (accent_800 >= 0 and accent_800 <= 16777215), - accent_900 integer CHECK (accent_900 >= 0 and accent_900 <= 16777215), - accent_a100 integer CHECK (accent_a100 >= 0 and accent_a100 <= 16777215), - accent_a200 integer CHECK (accent_a200 >= 0 and accent_a200 <= 16777215), - accent_a400 integer CHECK (accent_a400 >= 0 and accent_a400 <= 16777215), - accent_a700 integer CHECK (accent_a700 >= 0 and accent_a700 <= 16777215), - primary_100 integer CHECK (primary_100 >= 0 and primary_100 <= 16777215), - primary_200 integer CHECK (primary_200 >= 0 and primary_200 <= 16777215), - primary_300 integer CHECK (primary_300 >= 0 and primary_300 <= 16777215), - primary_400 integer CHECK (primary_400 >= 0 and primary_400 <= 16777215), - primary_50 integer CHECK (primary_50 >= 0 and primary_50 <= 16777215), - primary_500 integer CHECK (primary_500 >= 0 and primary_500 <= 16777215) NOT NULL, - primary_600 integer CHECK (primary_600 >= 0 and primary_600 <= 16777215), - primary_700 integer CHECK (primary_700 >= 0 and primary_700 <= 16777215), - primary_800 integer CHECK (primary_800 >= 0 and primary_800 <= 16777215), - primary_900 integer CHECK (primary_900 >= 0 and primary_900 <= 16777215), - primary_a100 integer CHECK (primary_a100 >= 0 and primary_a100 <= 16777215), - primary_a200 integer CHECK (primary_a200 >= 0 and primary_a200 <= 16777215), - primary_a400 integer CHECK (primary_a400 >= 0 and primary_a400 <= 16777215), - primary_a700 integer CHECK (primary_a700 >= 0 and primary_a700 <= 16777215), - warn_100 integer CHECK (warn_100 >= 0 and warn_100 <= 16777215), - warn_200 integer CHECK (warn_200 >= 0 and warn_200 <= 16777215), - warn_300 integer CHECK (warn_300 >= 0 and warn_300 <= 16777215), - warn_400 integer CHECK (warn_400 >= 0 and warn_400 <= 16777215), - warn_50 integer CHECK (warn_50 >= 0 and warn_50 <= 16777215), - warn_500 integer CHECK (warn_500 >= 0 and warn_500 <= 16777215) NOT NULL, - warn_600 integer CHECK (warn_600 >= 0 and warn_600 <= 16777215), - warn_700 integer CHECK (warn_700 >= 0 and warn_700 <= 16777215), - warn_800 integer CHECK (warn_800 >= 0 and warn_800 <= 16777215), - warn_900 integer CHECK (warn_900 >= 0 and warn_900 <= 16777215), - warn_a100 integer CHECK (warn_a100 >= 0 and warn_a100 <= 16777215), - warn_a200 integer CHECK (warn_a200 >= 0 and warn_a200 <= 16777215), - warn_a400 integer CHECK (warn_a400 >= 0 and warn_a400 <= 16777215), - warn_a700 integer CHECK (warn_a700 >= 0 and warn_a700 <= 16777215), - headbar integer CHECK (headbar >= 0 and headbar <= 16777215), - yes integer CHECK (yes >= 0 and yes <= 16777215), - no integer CHECK (no >= 0 and no <= 16777215), - abstain integer CHECK (abstain >= 0 and abstain <= 16777215), + accent_100 varchar(7) CHECK (accent_100 is null or accent_100 ~* '^#[a-f0-9]{6}$'), + accent_200 varchar(7) CHECK (accent_200 is null or accent_200 ~* '^#[a-f0-9]{6}$'), + accent_300 varchar(7) CHECK (accent_300 is null or accent_300 ~* '^#[a-f0-9]{6}$'), + accent_400 varchar(7) CHECK (accent_400 is null or accent_400 ~* '^#[a-f0-9]{6}$'), + accent_50 varchar(7) CHECK (accent_50 is null or accent_50 ~* '^#[a-f0-9]{6}$'), + accent_500 varchar(7) CHECK (accent_500 is null or accent_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + accent_600 varchar(7) CHECK (accent_600 is null or accent_600 ~* '^#[a-f0-9]{6}$'), + accent_700 varchar(7) CHECK (accent_700 is null or accent_700 ~* '^#[a-f0-9]{6}$'), + accent_800 varchar(7) CHECK (accent_800 is null or accent_800 ~* '^#[a-f0-9]{6}$'), + accent_900 varchar(7) CHECK (accent_900 is null or accent_900 ~* '^#[a-f0-9]{6}$'), + accent_a100 varchar(7) CHECK (accent_a100 is null or accent_a100 ~* '^#[a-f0-9]{6}$'), + accent_a200 varchar(7) CHECK (accent_a200 is null or accent_a200 ~* '^#[a-f0-9]{6}$'), + accent_a400 varchar(7) CHECK (accent_a400 is null or accent_a400 ~* '^#[a-f0-9]{6}$'), + accent_a700 varchar(7) CHECK (accent_a700 is null or accent_a700 ~* '^#[a-f0-9]{6}$'), + primary_100 varchar(7) CHECK (primary_100 is null or primary_100 ~* '^#[a-f0-9]{6}$'), + primary_200 varchar(7) CHECK (primary_200 is null or primary_200 ~* '^#[a-f0-9]{6}$'), + primary_300 varchar(7) CHECK (primary_300 is null or primary_300 ~* '^#[a-f0-9]{6}$'), + primary_400 varchar(7) CHECK (primary_400 is null or primary_400 ~* '^#[a-f0-9]{6}$'), + primary_50 varchar(7) CHECK (primary_50 is null or primary_50 ~* '^#[a-f0-9]{6}$'), + primary_500 varchar(7) CHECK (primary_500 is null or primary_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + primary_600 varchar(7) CHECK (primary_600 is null or primary_600 ~* '^#[a-f0-9]{6}$'), + primary_700 varchar(7) CHECK (primary_700 is null or primary_700 ~* '^#[a-f0-9]{6}$'), + primary_800 varchar(7) CHECK (primary_800 is null or primary_800 ~* '^#[a-f0-9]{6}$'), + primary_900 varchar(7) CHECK (primary_900 is null or primary_900 ~* '^#[a-f0-9]{6}$'), + primary_a100 varchar(7) CHECK (primary_a100 is null or primary_a100 ~* '^#[a-f0-9]{6}$'), + primary_a200 varchar(7) CHECK (primary_a200 is null or primary_a200 ~* '^#[a-f0-9]{6}$'), + primary_a400 varchar(7) CHECK (primary_a400 is null or primary_a400 ~* '^#[a-f0-9]{6}$'), + primary_a700 varchar(7) CHECK (primary_a700 is null or primary_a700 ~* '^#[a-f0-9]{6}$'), + warn_100 varchar(7) CHECK (warn_100 is null or warn_100 ~* '^#[a-f0-9]{6}$'), + warn_200 varchar(7) CHECK (warn_200 is null or warn_200 ~* '^#[a-f0-9]{6}$'), + warn_300 varchar(7) CHECK (warn_300 is null or warn_300 ~* '^#[a-f0-9]{6}$'), + warn_400 varchar(7) CHECK (warn_400 is null or warn_400 ~* '^#[a-f0-9]{6}$'), + warn_50 varchar(7) CHECK (warn_50 is null or warn_50 ~* '^#[a-f0-9]{6}$'), + warn_500 varchar(7) CHECK (warn_500 is null or warn_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + warn_600 varchar(7) CHECK (warn_600 is null or warn_600 ~* '^#[a-f0-9]{6}$'), + warn_700 varchar(7) CHECK (warn_700 is null or warn_700 ~* '^#[a-f0-9]{6}$'), + warn_800 varchar(7) CHECK (warn_800 is null or warn_800 ~* '^#[a-f0-9]{6}$'), + warn_900 varchar(7) CHECK (warn_900 is null or warn_900 ~* '^#[a-f0-9]{6}$'), + warn_a100 varchar(7) CHECK (warn_a100 is null or warn_a100 ~* '^#[a-f0-9]{6}$'), + warn_a200 varchar(7) CHECK (warn_a200 is null or warn_a200 ~* '^#[a-f0-9]{6}$'), + warn_a400 varchar(7) CHECK (warn_a400 is null or warn_a400 ~* '^#[a-f0-9]{6}$'), + warn_a700 varchar(7) CHECK (warn_a700 is null or warn_a700 ~* '^#[a-f0-9]{6}$'), + headbar varchar(7) CHECK (headbar is null or headbar ~* '^#[a-f0-9]{6}$'), + yes varchar(7) CHECK (yes is null or yes ~* '^#[a-f0-9]{6}$'), + no varchar(7) CHECK (no is null or no ~* '^#[a-f0-9]{6}$'), + abstain varchar(7) CHECK (abstain is null or abstain ~* '^#[a-f0-9]{6}$'), organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -246,7 +246,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( start_time timestamptz, end_time timestamptz, imported_at timestamptz, - language varchar(256) NOT NULL CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) CONSTRAINT minlength_language CHECK (char_length(language) >= 1), + language varchar(256) NOT NULL CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), jitsi_domain varchar(256), jitsi_room_name varchar(256), jitsi_room_password varchar(256), @@ -426,7 +426,7 @@ comment on column meeting_t.user_ids is 'Calculated. All user ids from all users CREATE TABLE IF NOT EXISTS structure_level_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - color integer CHECK (color >= 0 and color <= 16777215), + color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$'), default_time integer CONSTRAINT minimum_default_time CHECK (default_time >= 0), meeting_id integer NOT NULL ); @@ -949,13 +949,13 @@ CREATE TABLE IF NOT EXISTS projector_t ( width integer CONSTRAINT minimum_width CHECK (width >= 1) DEFAULT 1200, aspect_ratio_numerator integer CONSTRAINT minimum_aspect_ratio_numerator CHECK (aspect_ratio_numerator >= 1) DEFAULT 16, aspect_ratio_denominator integer CONSTRAINT minimum_aspect_ratio_denominator CHECK (aspect_ratio_denominator >= 1) DEFAULT 9, - color integer CHECK (color >= 0 and color <= 16777215) DEFAULT 0, - background_color integer CHECK (background_color >= 0 and background_color <= 16777215) DEFAULT 16777215, - header_background_color integer CHECK (header_background_color >= 0 and header_background_color <= 16777215) DEFAULT 3241878, - header_font_color integer CHECK (header_font_color >= 0 and header_font_color <= 16777215) DEFAULT 16119285, - header_h1_color integer CHECK (header_h1_color >= 0 and header_h1_color <= 16777215) DEFAULT 3241878, - chyron_background_color integer CHECK (chyron_background_color >= 0 and chyron_background_color <= 16777215) DEFAULT 3241878, - chyron_font_color integer CHECK (chyron_font_color >= 0 and chyron_font_color <= 16777215) DEFAULT 16777215, + color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') DEFAULT '#000000', + background_color varchar(7) CHECK (background_color is null or background_color ~* '^#[a-f0-9]{6}$') DEFAULT '#ffffff', + header_background_color varchar(7) CHECK (header_background_color is null or header_background_color ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', + header_font_color varchar(7) CHECK (header_font_color is null or header_font_color ~* '^#[a-f0-9]{6}$') DEFAULT '#f5f5f5', + header_h1_color varchar(7) CHECK (header_h1_color is null or header_h1_color ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', + chyron_background_color varchar(7) CHECK (chyron_background_color is null or chyron_background_color ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', + chyron_font_color varchar(7) CHECK (chyron_font_color is null or chyron_font_color ~* '^#[a-f0-9]{6}$') DEFAULT '#ffffff', show_header_footer boolean DEFAULT True, show_title boolean DEFAULT True, show_logo boolean DEFAULT True, diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 851d7744..a0e548c7 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -237,12 +237,10 @@ def get_schema_color( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> tuple[SchemaZoneTexts, str]: text = cast(SchemaZoneTexts, defaultdict(str)) - subst, tmp = Helper.get_initials(table_name, fname, type_, fdata) - text.update(tmp) + subst, szt = Helper.get_initials(table_name, fname, type_, fdata) + text.update(szt) tmpl = FIELD_TYPES[type_]["pg_type"] - subst["type"] = tmpl.substitute(subst) - if default := fdata.get("default"): - subst["default"] = f" DEFAULT {int(default[1:], 16)}" + subst["type"] = tmpl.substitute({"field_name": fname}) text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) return text, "" @@ -1217,7 +1215,7 @@ def get_definitions_from_foreign_list( }, "color": { "pg_type": string.Template( - "integer CHECK (${field_name} >= 0 and ${field_name} <= 16777215)" + "varchar(7) CHECK (${field_name} is null or ${field_name} ~* '^#[a-f0-9]{6}$$')" ), "method": GenerateCodeBlocks.get_schema_color, }, diff --git a/dev/tests/base.py b/dev/tests/base.py index 58c37028..35db733d 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -86,9 +86,9 @@ def populate_database(cls) -> None: with cls.db_connection.cursor() as curs: cls.theme1_id = DbUtils.insert_wrapper(curs, "theme_t", { "name": "OpenSlides Blue", - "accent_500": int("0x2196f3", 16), - "primary_500": int("0x317796", 16), - "warn_500": int("0xf06400", 16), + "accent_500": "#2196f3", + "primary_500": "#317796", + "warn_500": "#f06400", }) cls.organization_id = DbUtils.insert_wrapper(curs, "organization_t", { "name": "Test Organization", @@ -220,13 +220,13 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "width": 1220, "aspect_ratio_numerator": 4, "aspect_ratio_denominator": 3, - "color": int("0x000000", 16), - "background_color": int("0xffffff", 16), - "header_background_color": int("0x317796", 16), - "header_font_color": int("0xf5f5f5", 16), - "header_h1_color": int("0x317796", 16), - "chyron_background_color": int("0x317796", 16), - "chyron_font_color": int("0xffffff", 16), + "color": "#000000", + "background_color": "#ffffff", + "header_background_color": "#317796", + "header_font_color": "#f5f5f5", + "header_h1_color": "#317796", + "chyron_background_color": "#317796", + "chyron_font_color": "#ffffff", "show_header_footer": True, "show_title": True, "show_logo": True, @@ -256,13 +256,13 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "width": 1024, "aspect_ratio_numerator": 16, "aspect_ratio_denominator": 9, - "color": int("0x000000", 16), - "background_color": int("0x888888", 16), - "header_background_color": int("0x317796", 16), - "header_font_color": int("0xf5f5f5", 16), - "header_h1_color": int("0x317796", 16), - "chyron_background_color": int("0x317796", 16), - "chyron_font_color": int("0xffffff", 16), + "color": "#000000", + "background_color": "#888888", + "header_background_color": "#317796", + "header_font_color": "#f5f5f5", + "header_h1_color": "#317796", + "chyron_background_color": "#317796", + "chyron_font_color": "#ffffff", "show_header_footer": True, "show_title": True, "show_logo": True, diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 728a6458..def0d964 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -251,15 +251,15 @@ def test_generic_nGt_nt(self) -> None: tag_ids = DbUtils.insert_many_wrapper(curs, "organization_tag_t", [ { "name": "Orga Tag 1", - "color": 0xffee13 + "color": "#ffee13" }, { "name": "Orga Tag 2", - "color": 0x12ee13 + "color": "#12ee13" }, { "name": "Orga Tag 3", - "color": 0x00ee13 + "color": "#00ee13" }, ]) DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ @@ -282,7 +282,7 @@ def test_generic_nGt_check_constraint_error(self) -> None: with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": 0xffee13}) + tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": "#ffee13"}) DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}, {"organization_tag_id": tag_id, "tagged_id": f"motion_state/{self.meeting1_id}"}, @@ -292,7 +292,7 @@ def test_generic_nGt_check_constraint_error(self) -> None: def test_generic_nGt_unique_constraint_error(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": 0xffee13}) + tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": "#ffee13"}) DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.transaction(): @@ -351,3 +351,52 @@ def test_wrong_permissions_in_group(self) -> None: sql = tuple(group_t.update([group_t.permissions], [DbUtils.get_pg_array_for_cu(group["permissions"]),], where=group_t.id==1, returning=[group_t.permissions])) group = curs.execute(*sql).fetchone() assert 'violates check constraint "enum_group_permissions"' in str(e) + +class DataTypeTests(BaseTestCase): + def test_color_type_correct(self) -> None: + orga_tag_t = Table("organization_tag_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + orga_tags = curs.execute(*orga_tag_t.insert(columns=[orga_tag_t.name, orga_tag_t.color], values=[['Foo', '#ff12cc'], ["Bar", "#1234AA"]], returning=[orga_tag_t.id, orga_tag_t.name, orga_tag_t.color])).fetchall() + assert orga_tags[0] == {"id": 1, "name": "Foo", "color": "#ff12cc"} + assert orga_tags[1] == {"id": 2, "name": "Bar", "color": "#1234AA"} + + def test_color_type_not_null_error(self) -> None: + orga_tag_t = Table("organization_tag_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + with pytest.raises(psycopg.DatabaseError) as e: + curs.execute(*orga_tag_t.insert(columns=[orga_tag_t.name, orga_tag_t.color], values=[['Foo', None]], returning=[orga_tag_t.id, orga_tag_t.name, orga_tag_t.color])).fetchone() + assert 'null value in column "color" of relation "organization_tag_t" violates not-null constraint' in str(e) + + def test_color_type_null_correct(self) -> None: + sl_t = Table("structure_level_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + sl_id = curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', None, 1]], returning=[sl_t.id])).fetchone()["id"] + structure_level = curs.execute(*sl_t.select(sl_t.id, sl_t.color, where=sl_t.id==sl_id)).fetchone() + assert structure_level == {"id": sl_id, "color": None} + + def test_color_type_empty_string_error(self) -> None: + sl_t = Table("structure_level_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + with pytest.raises(psycopg.DatabaseError) as e: + curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', '', 1]], returning=[sl_t.id])).fetchone()["id"] + assert """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" in str(e) + + def test_color_type_wrong_string_error(self) -> None: + sl_t = Table("structure_level_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + with pytest.raises(psycopg.DatabaseError) as e: + curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', 'xxx', 1]], returning=[sl_t.id])).fetchone()["id"] + assert """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" in str(e) + + def test_color_type_to_long_string_error(self) -> None: + sl_t = Table("structure_level_t") + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + with pytest.raises(psycopg.DatabaseError) as e: + curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', '#1234567', 1]], returning=[sl_t.id])).fetchone()["id"] + assert """value too long for type character varying(7)""" in str(e) From e4c02dc1c961d1e4a419a6e68fef9ba0fbcdd3c9 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 17 Apr 2024 18:44:43 +0200 Subject: [PATCH 050/142] use python-sql for generating sql --- dev/Makefile | 13 +- dev/src/db_utils.py | 89 +-- dev/src/generate_sql_schema.py | 1 - dev/tests/base.py | 684 +++++++++++------ dev/tests/test_generic_relations.py | 1097 +++++++++++++++++++++------ models.yml | 1 - 6 files changed, 1351 insertions(+), 534 deletions(-) diff --git a/dev/Makefile b/dev/Makefile index d3a88cb8..7670ec8f 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -1,4 +1,5 @@ # Commands inside the container +paths = src/ tests/ all: pyupgrade black autoflake isort flake8 mypy @@ -9,22 +10,22 @@ check-pyupgrade: pyupgrade --py310-plus $$(find . -name '*.py') black: - black src/ + black $(paths) check-black: - black --check --diff src/ + black --check --diff $(paths) autoflake: - autoflake src/ + autoflake $(paths) isort: - isort src/ + isort $(paths) check-isort: - isort --check-only --diff src/ + isort --check-only --diff $(paths) flake8: - flake8 src/ + flake8 $(paths) mypy: mypy src/ diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index 46bc08bd..78633d29 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -1,83 +1,38 @@ -from typing import Any, cast +from typing import Any -from psycopg import Cursor, sql +from sql import Column, Table # type: ignore class DbUtils: @classmethod - def get_pg_array_for_cu(cls, data: list) -> str: - return f"""{{"{'","'.join(item for item in data)}"}}""" - - @classmethod - def insert_wrapper( - cls, curs: Cursor, table_name: str, data: dict[str, Any] - ) -> None | int: - query = f"INSERT INTO {table_name} ({', '.join(data.keys())}) VALUES({{}}) RETURNING id;" - query = ( - sql.SQL(query) - .format( - sql.SQL(", ").join(sql.Placeholder() * len(data.keys())), - ) - .as_string(curs) - ) - result = curs.execute(query, tuple(data.values())).fetchone() - if isinstance(result, dict): - return result.get("id", 0) - return None - - @classmethod - def insert_many_wrapper( + def get_columns_and_values_for_insert( cls, - curs: Cursor, - table_name: str, + table: Table, data_list: list[dict[str, Any]], - returning: str = "id", - ) -> list[int]: - ids: list[int] = [] + ) -> tuple[list[Column], list[list[dict[str, Any]]]]: + """ + takes a list of dicts, each one to be inserted + Takes care of columns and row positions and fills + not existent columns in row with "None" + """ + columns: list[Column] = [] + values: list[list[dict[str, Any]]] = [] if not data_list: - return ids + return columns, values # use all keys in same sequence keys_set: set = set() for data in data_list: keys_set.update(data.keys()) keys: list = sorted(keys_set) - temp_data = {k: None for k in keys} + columns = [Column(table, key) for key in keys] + values = [[row.get(k, None) for k in keys] for row in data_list] + return columns, values - dates = [temp_data | data for data in data_list] - query = f"INSERT INTO {table_name} ({', '.join(keys)}) VALUES({{}}){' RETURNING ' + returning if returning else ''};" - query = ( - sql.SQL(query) - .format( - sql.SQL(", ").join(sql.Placeholder() * len(keys)), - ) - .as_string(curs) - ) - curs.executemany( - query, - tuple(tuple(v for _, v in sorted(data.items())) for data in dates), - returning=bool(returning), - ) - ids = [] - if returning: - while True: - ids.append(cast(dict, curs.fetchone())[returning]) - if not curs.nextset(): - break - return ids + @classmethod + def get_columns_from_list(cls, table: Table, items: list[str]) -> list[Column]: + return [Column(table, item) for item in items] @classmethod - def select_id_wrapper( - cls, - curs: Cursor, - table_name: str, - id_: int | None = None, - field_names: list[str] = [], - ) -> dict[str, Any] | list[dict[str, Any]]: - """select with single id or all for fields in list or all fields""" - query = sql.SQL( - f"SELECT {', '.join(field_names) if field_names else '*'} FROM {table_name}{' where id = %s' if id_ else ''}" - ) - if id_: - return result if (result := curs.execute(query, (id_,)).fetchone()) else {} - else: - return curs.execute(query).fetchall() + def get_pg_array_for_cu(cls, data: list) -> str: + """converts a value list into string used for complete array field""" + return f"""{{"{'","'.join(item for item in data)}"}}""" diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index a0e548c7..249c3b08 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1060,7 +1060,6 @@ def generate_field_or_sql_decision( ("1t", "1rR"): (FieldSqlErrorType.SQL, False), ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), - ("1rR", "1t"): (FieldSqlErrorType.FIELD, False), ("nGt", "nt"): (FieldSqlErrorType.SQL, True), ("nr", ""): (FieldSqlErrorType.SQL, True), ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), diff --git a/dev/tests/base.py b/dev/tests/base.py index 35db733d..cd2aaaaa 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -1,24 +1,25 @@ import os -from datetime import datetime -from typing import Any +from collections.abc import Callable from unittest import TestCase import psycopg -import pytest from psycopg import sql from psycopg.types.json import Jsonb + +from sql import Table from src.db_utils import DbUtils # ADMIN_USERNAME = "admin" # ADMIN_PASSWORD = "admin" + class BaseTestCase(TestCase): temporary_template_db = "openslides_template" work_on_test_db = "openslides_test" db_connection: psycopg.Connection = None # id's of pre loaded rows, see method populate_database - meeting1_id= 0 + meeting1_id = 0 theme1_id = 0 organization_id = 0 user1_id = 0 @@ -30,10 +31,19 @@ class BaseTestCase(TestCase): complex_workflowM1_id = 0 @classmethod - def set_db_connection(cls, db_name:str, autocommit:bool = False, row_factory:callable = psycopg.rows.dict_row) -> None: + def set_db_connection( + cls, + db_name: str, + autocommit: bool = False, + row_factory: Callable = psycopg.rows.dict_row, + ) -> None: env = os.environ try: - cls.db_connection = psycopg.connect(f"dbname='{db_name}' user='{env['DATABASE_USER']}' host='{env['DATABASE_HOST']}' password='{env['PGPASSWORD']}'", autocommit=autocommit, row_factory=row_factory) + cls.db_connection = psycopg.connect( + f"dbname='{db_name}' user='{env['DATABASE_USER']}' host='{env['DATABASE_HOST']}' password='{env['PGPASSWORD']}'", + autocommit=autocommit, + row_factory=row_factory, + ) cls.db_connection.isolation_level = psycopg.IsolationLevel.SERIALIZABLE except Exception as e: raise Exception(f"Cannot connect to postgres: {e.message}") @@ -45,97 +55,156 @@ def setup_class(cls): with cls.db_connection: with cls.db_connection.cursor() as curs: curs.execute( - sql.SQL("DROP DATABASE IF EXISTS {temporary_template_db} (FORCE);").format( + sql.SQL( + "DROP DATABASE IF EXISTS {temporary_template_db} (FORCE);" + ).format( temporary_template_db=sql.Identifier(cls.temporary_template_db) ) ) curs.execute( - sql.SQL("CREATE DATABASE {db_to_create} TEMPLATE {template_db};").format( + sql.SQL( + "CREATE DATABASE {db_to_create} TEMPLATE {template_db};" + ).format( db_to_create=sql.Identifier(cls.temporary_template_db), - template_db=sql.Identifier(env['DATABASE_NAME']))) + template_db=sql.Identifier(env["DATABASE_NAME"]), + ) + ) cls.set_db_connection(cls.temporary_template_db) with cls.db_connection: with cls.db_connection.cursor() as curs: - curs.execute("CREATE EXTENSION pldbgapi;") # Postgres debug extension + curs.execute("CREATE EXTENSION pldbgapi;") # Postgres debug extension cls.populate_database() @classmethod def teardown_class(cls): - """ remove last test db and drop the temporary template db""" + """remove last test db and drop the temporary template db""" cls.set_db_connection("postgres", True) with cls.db_connection: with cls.db_connection.cursor() as curs: - curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(cls.work_on_test_db))) - curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(cls.temporary_template_db))) + curs.execute( + sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format( + sql.Identifier(cls.work_on_test_db) + ) + ) + curs.execute( + sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format( + sql.Identifier(cls.temporary_template_db) + ) + ) def setUp(self) -> None: self.set_db_connection("postgres", autocommit=True) with self.db_connection: with self.db_connection.cursor() as curs: - curs.execute(sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format(sql.Identifier(self.work_on_test_db))) - curs.execute(sql.SQL("CREATE DATABASE {test_db} TEMPLATE {temporary_template_db};").format( - test_db=sql.Identifier(self.work_on_test_db), - temporary_template_db=sql.Identifier(self.temporary_template_db))) + curs.execute( + sql.SQL("DROP DATABASE IF EXISTS {} (FORCE);").format( + sql.Identifier(self.work_on_test_db) + ) + ) + curs.execute( + sql.SQL( + "CREATE DATABASE {test_db} TEMPLATE {temporary_template_db};" + ).format( + test_db=sql.Identifier(self.work_on_test_db), + temporary_template_db=sql.Identifier( + self.temporary_template_db + ), + ) + ) self.set_db_connection(self.work_on_test_db) @classmethod def populate_database(cls) -> None: - """ do something like setting initial_data.json""" + """do something like setting initial_data.json""" + theme_t = Table("theme_t") + organization_t = Table("organization_t") + user_t = Table("user_t") + committee_t = Table("committee_t") + with cls.db_connection.transaction(): with cls.db_connection.cursor() as curs: - cls.theme1_id = DbUtils.insert_wrapper(curs, "theme_t", { - "name": "OpenSlides Blue", - "accent_500": "#2196f3", - "primary_500": "#317796", - "warn_500": "#f06400", - }) - cls.organization_id = DbUtils.insert_wrapper(curs, "organization_t", { - "name": "Test Organization", - "legal_notice": "OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.", - "login_text": "Good Morning!", - "default_language": "en", - "genders": ["male", "female", "diverse", "non-binary"], - "enable_electronic_voting": True, - "enable_chat": True, - "reset_password_verbose_errors": True, - "limit_of_meetings": 0, - "limit_of_users": 0, - "theme_id": cls.theme1_id, - "users_email_sender": "OpenSlides", - "users_email_subject": "OpenSlides access data", - "users_email_body": "Dear {name},\n\nthis is your personal OpenSlides login:\n\n{url}\nUsername: {username}\nPassword: {password}\n\n\nThis email was generated automatically.", - "url": "https://example.com", - "saml_enabled": False, - "saml_login_button_text": "SAML Login", - "saml_attr_mapping": Jsonb({ - "saml_id": "username", - "title": "title", - "first_name": "firstName", - "last_name": "lastName", - "email": "email", - "gender": "gender", - "pronoun": "pronoun", - "is_active": "is_active", - "is_physical_person": "is_person" - }) - }) - cls.user1_id = DbUtils.insert_wrapper(curs, "user_t", { - "username": "admin", - "last_name": "Administrator", - "is_active": True, - "is_physical_person": True, - "password": "316af7b2ddc20ead599c38541fbe87e9a9e4e960d4017d6e59de188b41b2758flD5BVZAZ8jLy4nYW9iomHcnkXWkfk3PgBjeiTSxjGG7+fBjMBxsaS1vIiAMxYh+K38l0gDW4wcP+i8tgoc4UBg==", - "default_password": "admin", - "can_change_own_password": True, - "gender": "male", - "default_vote_weight": "1.000000", - "organization_management_level": "superadmin", - }) - cls.committee1_id = DbUtils.insert_wrapper(curs, "committee_t", { - "name": "Default committee", - "description": "Add description here", - }) + cls.theme1_id = curs.execute( + *theme_t.insert( + columns=[ + theme_t.name, + theme_t.accent_500, + theme_t.primary_500, + theme_t.warn_500, + ], + values=[["OpenSlides Blue", "#2196f3", "#317796", "#f06400"]], + returning=[theme_t.id], + ) + ).fetchone()["id"] + data = [ + { + "name": "Test Organization", + "legal_notice": 'OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.', + "login_text": "Good Morning!", + "default_language": "en", + "genders": ["male", "female", "diverse", "non-binary"], + "enable_electronic_voting": True, + "enable_chat": True, + "reset_password_verbose_errors": True, + "limit_of_meetings": 0, + "limit_of_users": 0, + "theme_id": cls.theme1_id, + "users_email_sender": "OpenSlides", + "users_email_subject": "OpenSlides access data", + "users_email_body": "Dear {name},\n\nthis is your personal OpenSlides login:\n\n{url}\nUsername: {username}\nPassword: {password}\n\n\nThis email was generated automatically.", + "url": "https://example.com", + "saml_enabled": False, + "saml_login_button_text": "SAML Login", + "saml_attr_mapping": Jsonb( + { + "saml_id": "username", + "title": "title", + "first_name": "firstName", + "last_name": "lastName", + "email": "email", + "gender": "gender", + "pronoun": "pronoun", + "is_active": "is_active", + "is_physical_person": "is_person", + } + ), + } + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + organization_t, data + ) + cls.organization_id = curs.execute( + *organization_t.insert( + columns, values, returning=[organization_t.id] + ) + ).fetchone()["id"] + data = [ + { + "username": "admin", + "last_name": "Administrator", + "is_active": True, + "is_physical_person": True, + "password": "316af7b2ddc20ead599c38541fbe87e9a9e4e960d4017d6e59de188b41b2758flD5BVZAZ8jLy4nYW9iomHcnkXWkfk3PgBjeiTSxjGG7+fBjMBxsaS1vIiAMxYh+K38l0gDW4wcP+i8tgoc4UBg==", + "default_password": "admin", + "can_change_own_password": True, + "gender": "male", + "default_vote_weight": "1.000000", + "organization_management_level": "superadmin", + } + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + user_t, data + ) + cls.user1_id = curs.execute( + *user_t.insert(columns, values, returning=[user_t.id]) + ).fetchone()["id"] + cls.committee1_id = curs.execute( + *committee_t.insert( + columns=[committee_t.name, committee_t.description], + values=[["Default committee", "Add description here"]], + returning=[committee_t.id], + ) + ).fetchone()["id"] result_ids = cls.create_meeting(curs, committee_id=cls.committee1_id) cls.meeting1_id = result_ids["meeting_id"] cls.groupM1_default_id = result_ids["default_group_id"] @@ -143,10 +212,20 @@ def populate_database(cls) -> None: cls.groupM1_staff_id = result_ids["staff_group_id"] cls.simple_workflowM1_id = result_ids["simple_workflow_id"] cls.complex_workflowM1_id = result_ids["complex_workflow_id"] - curs.execute("UPDATE committee_t SET default_meeting_id = %s where id = %s;", (result_ids["meeting_id"], cls.committee1_id)) + curs.execute( + *committee_t.update( + columns=[committee_t.default_meeting_id], + values=[cls.committee1_id], + ) + ) @classmethod - def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int = 0, ) -> None: + def create_meeting( + cls, + curs: psycopg.Cursor, + committee_id: int, + meeting_id: int = 0, + ) -> dict[str, int]: """ Creates meeting with next availale id if not set or id set (lower ids can't be choosed afterwards) The committee_id must be given and the committee must exist. The meeting will not be set as default meeting of the committee @@ -159,59 +238,90 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int - default_project_id, secondary_projector_id - simple_workflow_id, complex_workflow_id """ + group_t = Table("group_t") + projector_t = Table("projector_t") + motion_state_t = Table("motion_state_t") + nm_motion_state_next_state_ids_motion_state_t = Table( + "nm_motion_state_next_state_ids_motion_state_t" + ) + motion_workflow_t = Table("motion_workflow_t") + meeting_t = Table("meeting_t") + result = {} if meeting_id: - sequence_name = curs.execute("select * from pg_get_serial_sequence('meeting_t', 'id');").fetchone()["pg_get_serial_sequence"] - last_value = curs.execute(f"select last_value from {sequence_name};").fetchone()["last_value"] + sequence_name = curs.execute( + "select * from pg_get_serial_sequence('meeting_t', 'id');" + ).fetchone()["pg_get_serial_sequence"] + last_value = curs.execute( + f"select last_value from {sequence_name};" + ).fetchone()["last_value"] if last_value >= meeting_id: - raise ValueError(f"meeting_id {meeting_id} is not available, last_value in sequence {sequence_name} is {last_value}") - result["meeting_id"] = curs.execute("select setval(pg_get_serial_sequence('meeting_t', 'id'), %s);", (meeting_id,)).fetchone()["setval"] + raise ValueError( + f"meeting_id {meeting_id} is not available, last_value in sequence {sequence_name} is {last_value}" + ) + result["meeting_id"] = curs.execute( + "select setval(pg_get_serial_sequence('meeting_t', 'id'), %s);", + (meeting_id,), + ).fetchone()["setval"] else: - result["meeting_id"] = curs.execute("select nextval(pg_get_serial_sequence('meeting_t', 'id')) as id_;").fetchone()["id_"] - (result["default_group_id"], result["admin_group_id"], result["staff_group_id"]) = DbUtils.insert_many_wrapper(curs, "group_t", [ - { - "name": "Default", - "permissions": [ - "agenda_item.can_see_internal", - "assignment.can_see", - "list_of_speakers.can_see", - "mediafile.can_see", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see", - "user.can_see" + result["meeting_id"] = curs.execute( + "select nextval(pg_get_serial_sequence('meeting_t', 'id')) as id_;" + ).fetchone()["id_"] + curs.execute( + *group_t.insert( + columns=[ + group_t.name, + group_t.permissions, + group_t.weight, + group_t.meeting_id, ], - "weight": 1, - "meeting_id": result["meeting_id"] - }, - { - "name": "Admin", - "permissions": [], - "weight": 2, - "meeting_id": result["meeting_id"] - }, - { - "name": "Staff", - "permissions": [ - "agenda_item.can_manage", - "assignment.can_manage", - "assignment.can_nominate_self", - "list_of_speakers.can_be_speaker", - "list_of_speakers.can_manage", - "mediafile.can_manage", - "meeting.can_see_frontpage", - "meeting.can_see_history", - "motion.can_manage", - "poll.can_manage", - "projector.can_manage", - "tag.can_manage", - "user.can_manage" + values=[ + [ + "Default", + [ + "agenda_item.can_see_internal", + "assignment.can_see", + "list_of_speakers.can_see", + "mediafile.can_see", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see", + "user.can_see", + ], + 1, + result["meeting_id"], + ], + ["Admin", [], 2, result["meeting_id"]], + [ + "Staff", + [ + "agenda_item.can_manage", + "assignment.can_manage", + "assignment.can_nominate_self", + "list_of_speakers.can_be_speaker", + "list_of_speakers.can_manage", + "mediafile.can_manage", + "meeting.can_see_frontpage", + "meeting.can_see_history", + "motion.can_manage", + "poll.can_manage", + "projector.can_manage", + "tag.can_manage", + "user.can_manage", + ], + 3, + result["meeting_id"], + ], ], - "weight": 3, - "meeting_id": result["meeting_id"] - } - ]) - (result["default_projector_id"], result["secondary_projector_id"]) = DbUtils.insert_many_wrapper(curs, "projector_t", [ + returning=[group_t.id], + ) + ) + ( + result["default_group_id"], + result["admin_group_id"], + result["staff_group_id"], + ) = (x["id"] for x in curs) + data = [ { "name": "Default projector", "is_internal": False, @@ -232,21 +342,49 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "show_logo": True, "show_clock": True, "sequential_number": 1, - "used_as_default_projector_for_agenda_item_list_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_topic_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_list_of_speakers_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_current_los_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_motion_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_amendment_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_motion_block_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_assignment_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_mediafile_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_message_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_countdown_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_assignment_poll_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_motion_poll_in_meeting_id": result["meeting_id"], - "used_as_default_projector_for_poll_in_meeting_id": result["meeting_id"], - "meeting_id": result["meeting_id"] + "used_as_default_projector_for_agenda_item_list_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_topic_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_list_of_speakers_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_current_los_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_motion_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_amendment_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_motion_block_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_assignment_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_mediafile_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_message_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_countdown_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_assignment_poll_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_motion_poll_in_meeting_id": result[ + "meeting_id" + ], + "used_as_default_projector_for_poll_in_meeting_id": result[ + "meeting_id" + ], + "meeting_id": result["meeting_id"], }, { "name": "Secondary projector", @@ -268,12 +406,22 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "show_logo": True, "show_clock": True, "sequential_number": 2, - "meeting_id": result["meeting_id"] - } - ]) - result["simple_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;").fetchone()["new_id"] - result["complex_workflow_id"] = curs.execute("select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;").fetchone()["new_id"] - wf_m1_simple_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_state_t", [ + "meeting_id": result["meeting_id"], + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert(projector_t, data) + curs.execute(*projector_t.insert(columns, values, returning=[projector_t.id])) + (result["default_projector_id"], result["secondary_projector_id"]) = ( + x["id"] for x in curs + ) + + result["simple_workflow_id"] = curs.execute( + "select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;" + ).fetchone()["new_id"] + result["complex_workflow_id"] = curs.execute( + "select nextval(pg_get_serial_sequence('motion_workflow_t', 'id')) as new_id;" + ).fetchone()["new_id"] + motion_state_data = [ { "name": "submitted", "weight": 1, @@ -289,7 +437,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "show_recommendation_extension_field": False, "set_workflow_timestamp": True, "allow_motion_forwarding": True, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "accepted", @@ -307,7 +455,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "set_workflow_timestamp": False, "allow_motion_forwarding": True, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "rejected", @@ -325,7 +473,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "not decided", @@ -343,11 +491,19 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, - ]) + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + motion_state_t, motion_state_data + ) + curs.execute( + *motion_state_t.insert(columns, values, returning=[motion_state_t.id]) + ) + wf_m1_simple_motion_state_ids = [x["id"] for x in curs] wf_m1_simple_first_state_id = wf_m1_simple_motion_state_ids[0] - wf_m1_complex_motion_state_ids = DbUtils.insert_many_wrapper(curs, "motion_state_t", [ + + motion_state_data = [ { "name": "in progress", "weight": 5, @@ -363,7 +519,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "set_workflow_timestamp": True, "allow_motion_forwarding": True, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "submitted", @@ -380,7 +536,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": True, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "permitted", @@ -398,7 +554,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": 1 + "meeting_id": 1, }, { "name": "accepted", @@ -416,7 +572,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "rejected", @@ -434,7 +590,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "withdrawn", @@ -451,7 +607,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "adjourned", @@ -469,7 +625,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "not concerned", @@ -487,7 +643,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "referred to committee", @@ -505,7 +661,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "needs review", @@ -522,7 +678,7 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, { "name": "rejected (not authorized)", @@ -540,76 +696,154 @@ def create_meeting(cls, curs: psycopg.Cursor, committee_id: int, meeting_id: int "allow_support": False, "allow_motion_forwarding": True, "set_workflow_timestamp": False, - "meeting_id": result["meeting_id"] + "meeting_id": result["meeting_id"], }, - ]) + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + motion_state_t, motion_state_data + ) + curs.execute( + *motion_state_t.insert(columns, values, returning=[motion_state_t.id]) + ) + wf_m1_complex_motion_state_ids = [x["id"] for x in curs] wf_m1_complex_first_state_id = wf_m1_complex_motion_state_ids[0] - DbUtils.insert_many_wrapper(curs, "nm_motion_state_next_state_ids_motion_state_t", - [ - {"next_state_id": wf_m1_simple_motion_state_ids[1], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, - {"next_state_id": wf_m1_simple_motion_state_ids[2], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, - {"next_state_id": wf_m1_simple_motion_state_ids[3], "previous_state_id": wf_m1_simple_motion_state_ids[0]}, - {"next_state_id": wf_m1_complex_motion_state_ids[1], "previous_state_id": wf_m1_complex_motion_state_ids[0]}, - {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[0]}, - {"next_state_id": wf_m1_complex_motion_state_ids[2], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, - {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, - {"next_state_id": wf_m1_complex_motion_state_ids[10], "previous_state_id": wf_m1_complex_motion_state_ids[1]}, - {"next_state_id": wf_m1_complex_motion_state_ids[3], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[4], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[5], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[6], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[7], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[8], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - {"next_state_id": wf_m1_complex_motion_state_ids[9], "previous_state_id": wf_m1_complex_motion_state_ids[2]}, - ], returning='') - assert [result["simple_workflow_id"], result["complex_workflow_id"]] == DbUtils.insert_many_wrapper(curs, "motion_workflow_t", - [ - { - "id": result["simple_workflow_id"], - "name": "Simple Workflow", - "sequential_number": 1, - "first_state_id": wf_m1_simple_first_state_id, - "meeting_id": result["meeting_id"] - }, - { - "id": result["complex_workflow_id"], - "name": "Complex Workflow", - "sequential_number": 2, - "first_state_id": wf_m1_complex_first_state_id, - "meeting_id": result["meeting_id"] - } - ] + data = [ + { + "next_state_id": wf_m1_simple_motion_state_ids[1], + "previous_state_id": wf_m1_simple_motion_state_ids[0], + }, + { + "next_state_id": wf_m1_simple_motion_state_ids[2], + "previous_state_id": wf_m1_simple_motion_state_ids[0], + }, + { + "next_state_id": wf_m1_simple_motion_state_ids[3], + "previous_state_id": wf_m1_simple_motion_state_ids[0], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[1], + "previous_state_id": wf_m1_complex_motion_state_ids[0], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[5], + "previous_state_id": wf_m1_complex_motion_state_ids[0], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[2], + "previous_state_id": wf_m1_complex_motion_state_ids[1], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[5], + "previous_state_id": wf_m1_complex_motion_state_ids[1], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[10], + "previous_state_id": wf_m1_complex_motion_state_ids[1], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[3], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[4], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[5], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[6], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[7], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[8], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + { + "next_state_id": wf_m1_complex_motion_state_ids[9], + "previous_state_id": wf_m1_complex_motion_state_ids[2], + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + nm_motion_state_next_state_ids_motion_state_t, data + ) + curs.execute( + *nm_motion_state_next_state_ids_motion_state_t.insert(columns, values) + ) + + data = [ + { + "id": result["simple_workflow_id"], + "name": "Simple Workflow", + "sequential_number": 1, + "first_state_id": wf_m1_simple_first_state_id, + "meeting_id": result["meeting_id"], + }, + { + "id": result["complex_workflow_id"], + "name": "Complex Workflow", + "sequential_number": 2, + "first_state_id": wf_m1_complex_first_state_id, + "meeting_id": result["meeting_id"], + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + motion_workflow_t, data + ) + curs.execute( + *motion_workflow_t.insert(columns, values, returning=[motion_workflow_t.id]) + ) + assert [ + result["simple_workflow_id"], + result["complex_workflow_id"], + ] == [x["id"] for x in curs] + + data = [ + { + "id": result["meeting_id"], + "name": "OpenSlides Demo", + "is_active_in_organization_id": cls.organization_id, + "language": "en", + "conference_los_restriction": True, + "agenda_number_prefix": "TOP", + "motions_default_workflow_id": result["simple_workflow_id"], + "motions_default_amendment_workflow_id": result["complex_workflow_id"], + "motions_default_statute_amendment_workflow_id": result[ + "complex_workflow_id" + ], + "motions_recommendations_by": "ABK", + "motions_statute_recommendations_by": "", + "motions_statutes_enabled": True, + "motions_amendments_of_amendments": True, + "motions_amendments_prefix": "-\u00c4", + "motions_supporters_min_amount": 1, + "motions_export_preamble": "", + "users_enable_presence_view": True, + "users_pdf_wlan_encryption": "", + "users_enable_vote_delegations": True, + "poll_ballot_paper_selection": "CUSTOM_NUMBER", + "poll_ballot_paper_number": 8, + "poll_sort_poll_result_by_votes": True, + "poll_default_type": "nominal", + "poll_default_method": "votes", + "poll_default_onehundred_percent_base": "valid", + "committee_id": committee_id, + "reference_projector_id": result["default_projector_id"], + "default_group_id": result["default_group_id"], + "admin_group_id": result["admin_group_id"], + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert(meeting_t, data) + assert ( + result["meeting_id"] + == curs.execute( + *meeting_t.insert(columns, values, returning=[meeting_t.id]) + ).fetchone()["id"] ) - assert result["meeting_id"] == DbUtils.insert_wrapper(curs, "meeting_t", { - "id": result["meeting_id"], - "name": "OpenSlides Demo", - "is_active_in_organization_id": cls.organization_id, - "language": "en", - "conference_los_restriction": True, - "agenda_number_prefix": "TOP", - "motions_default_workflow_id": result["simple_workflow_id"], - "motions_default_amendment_workflow_id": result["complex_workflow_id"], - "motions_default_statute_amendment_workflow_id": result["complex_workflow_id"], - "motions_recommendations_by": "ABK", - "motions_statute_recommendations_by": "", - "motions_statutes_enabled": True, - "motions_amendments_of_amendments": True, - "motions_amendments_prefix": "-\u00c4", - "motions_supporters_min_amount": 1, - "motions_export_preamble": "", - "users_enable_presence_view": True, - "users_pdf_wlan_encryption": "", - "users_enable_vote_delegations": True, - "poll_ballot_paper_selection": "CUSTOM_NUMBER", - "poll_ballot_paper_number": 8, - "poll_sort_poll_result_by_votes": True, - "poll_default_type": "nominal", - "poll_default_method": "votes", - "poll_default_onehundred_percent_base": "valid", - "committee_id": committee_id, - "reference_projector_id": result["default_projector_id"], - "default_group_id": result["default_group_id"], - "admin_group_id": result["admin_group_id"] - }) - return result \ No newline at end of file + return result diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index def0d964..7a3f67c3 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -1,15 +1,33 @@ -from datetime import datetime -from typing import cast - import psycopg import pytest from psycopg import sql + from sql import Table -from sql.aggregate import * -from sql.conditionals import * from src.db_utils import DbUtils from tests.base import BaseTestCase +agenda_item_t = Table("agenda_item_t") +assignment_t = Table("assignment_t") +assignment_v = Table("assignment") +committee_v = Table("committee") +gm_organization_tag_tagged_ids_t = Table("gm_organization_tag_tagged_ids_t") +group_v = Table("group_") +group_t = Table("group_t") +list_of_speakers_t = Table("list_of_speakers_t") +list_of_speakers_v = Table("list_of_speakers") +mediafile_t = Table("mediafile_t") +mediafile_v = Table("mediafile") +meeting_t = Table("meeting_t") +meeting_v = Table("meeting") +option_t = Table("option_t") +organization_tag_t = Table("organization_tag_t") +organization_v = Table("organization") +poll_candidate_list_t = Table("poll_candidate_list_t") +poll_candidate_list_v = Table("poll_candidate_list") +projector_t = Table("projector") +theme_v = Table("theme") +user_v = Table("user_") + class Relations(BaseTestCase): """ @@ -25,289 +43,810 @@ class Relations(BaseTestCase): R: Required """ + """ 1:n relation tests with n-side NOT NULL """ + """ Test:motion_state.submitter_withdraw_back_ids: sql okay?""" """ 1:1 relation tests """ - def test_one_to_one_pre_populated_1rR_1t(self) -> None: + + # todo: 1Gr errors + def test_generic_1Gr_check_constraint_error(self) -> None: + """tries to use a not defined owner-model for generic field owner_id""" + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + curs.execute( + *mediafile_t.insert( + [mediafile_t.is_public, mediafile_t.owner_id], + [[True, f"motion_state/{self.meeting1_id}"]], + ) + ) + assert ( + 'new row for relation "mediafile_t" violates check constraint "valid_owner_id_part1"' + in str(e) + ) + + # todo: 1GrR errors + # todo: 1r errors + # todo: 1rR errors + + # todo: 1t:1GrR + def test_o2o_generic_1t_1GrR_okay(self) -> None: + """SQL 1t:1GrR => assignment/agenda_item_id:-> agenda_item/content_object_id""" with self.db_connection.cursor() as curs: - organization_row = DbUtils.select_id_wrapper(curs, "organization", self.organization_id, ["theme_id"]) - assert organization_row["theme_id"] == self.theme1_id - theme_row = DbUtils.select_id_wrapper(curs, "theme", self.theme1_id, ["theme_for_organization_id"]) - assert theme_row["theme_for_organization_id"] == self.organization_id + with self.db_connection.transaction(): + assignment_id = curs.execute( + *assignment_t.insert( + [ + assignment_t.title, + assignment_t.sequential_number, + assignment_t.meeting_id, + ], + [["title assignment 1", 21, self.meeting1_id]], + returning=[assignment_t.id], + ) + ).fetchone()["id"] + assignment = f"assignment/{assignment_id}" + agenda_item_id = curs.execute( + *agenda_item_t.insert( + [ + agenda_item_t.item_number, + agenda_item_t.content_object_id, + agenda_item_t.meeting_id, + ], + [["100", assignment, self.meeting1_id]], + returning=[agenda_item_t.id], + ) + ).fetchone()["id"] + assignment_row = curs.execute( + *assignment_v.select( + where=assignment_v.agenda_item_id == agenda_item_id + ) + ).fetchone() + agenda_item_row = curs.execute( + *agenda_item_t.select( + where=agenda_item_t.content_object_id == assignment + ) + ).fetchone() + assert assignment_row["agenda_item_id"] == agenda_item_row["id"] + assert agenda_item_row["content_object_id"] == assignment + assert ( + agenda_item_row["content_object_id_assignment_id"] == 1 + ) # internal storage - def test_one_to_one_pre_populated_1r_1t(self) -> None: + def test_o2o_generic_1t_1GrR_okay_with_setval(self) -> None: with self.db_connection.cursor() as curs: - committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["default_meeting_id"]) - assert committee_row["default_meeting_id"] == self.meeting1_id - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_meeting_for_committee_id"]) - assert meeting_row["default_meeting_for_committee_id"] == self.committee1_id + with self.db_connection.transaction(): + mediafile_id = 2 + los_id = 3 + curs.execute( + "select setval(pg_get_serial_sequence('mediafile_t', 'id'), %s);", + (mediafile_id,), + ) + assert ( + mediafile_id + == curs.execute( + *mediafile_t.insert( + [ + mediafile_t.id, + mediafile_t.is_public, + mediafile_t.owner_id, + ], + [[mediafile_id, True, f"meeting/{self.meeting1_id}"]], + returning=[mediafile_t.id], + ) + ).fetchone()["id"] + ) + curs.execute( + "select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", + (los_id,), + ) + data = [ + { + "id": los_id, + "content_object_id": ( + content_object_id := f"mediafile/{mediafile_id}" + ), + "meeting_id": self.meeting1_id, + "sequential_number": 28, + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + list_of_speakers_t, data + ) + assert ( + los_id + == curs.execute( + *list_of_speakers_t.insert( + columns, values, returning=[list_of_speakers_t.id] + ) + ).fetchone()["id"] + ) + los_row = curs.execute( + *list_of_speakers_t.select( + list_of_speakers_t.id, + list_of_speakers_t.content_object_id, + list_of_speakers_t.content_object_id_mediafile_id, + list_of_speakers_t.content_object_id_topic_id, + where=list_of_speakers_t.id == los_id, + ) + ).fetchone() + assert los_row["id"] == los_id + assert los_row["content_object_id"] == content_object_id + assert los_row["content_object_id_mediafile_id"] == mediafile_id + assert los_row["content_object_id_topic_id"] is None + + mediafile_row = curs.execute( + *mediafile_v.select( + mediafile_v.id, + mediafile_v.list_of_speakers_id, + mediafile_v.owner_id, + where=mediafile_v.id == mediafile_id, + ) + ).fetchone() + assert mediafile_row["id"] == mediafile_id + assert mediafile_row["list_of_speakers_id"] == los_id - # TODO: remove test, fiktiv test with 1r:1tR, test s.o, in der die meeting-Seite ein SQL hat - # jetzt setze ich mal ein erequired auf der Meeting Seite. Was ist übrigens mit 1r:1rR - def test_one_to_one_pre_populated_1r_1tR(self) -> None: + # todo: 1t:1r + def test_o2o_pre_populated_1t_1r_okay(self) -> None: with self.db_connection.cursor() as curs: - committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["default_meeting_id"]) + committee_row = curs.execute( + *committee_v.select( + committee_v.default_meeting_id, + where=committee_v.id == self.committee1_id, + ) + ).fetchone() assert committee_row["default_meeting_id"] == self.meeting1_id - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_meeting_for_committee_id"]) + meeting_row = curs.execute( + *meeting_v.select( + meeting_v.default_meeting_for_committee_id, + where=meeting_v.id == self.meeting1_id, + ) + ).fetchone() assert meeting_row["default_meeting_for_committee_id"] == self.committee1_id - def test_one_to_one_1tR_1t(self) -> None: + # todo: 1t:1rR + def test_o2o_pre_populated_1t_1rR_okay(self) -> None: + with self.db_connection.cursor() as curs: + organization_row = curs.execute( + *organization_v.select( + organization_v.theme_id, + where=organization_v.id == self.organization_id, + ) + ).fetchone() + assert organization_row["theme_id"] == self.theme1_id + theme_row = curs.execute( + *theme_v.select( + theme_v.theme_for_organization_id, + where=theme_v.id == self.theme1_id, + ) + ).fetchone() + assert theme_row["theme_for_organization_id"] == self.organization_id + + def test_o2o_1t_1rR_okay_with_change_data(self) -> None: with self.db_connection.cursor() as curs: # Prepopulated - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) + meeting_row = curs.execute( + *meeting_v.select( + meeting_v.default_group_id, where=meeting_v.id == self.meeting1_id + ) + ).fetchone() old_default_group_id = meeting_row["default_group_id"] - old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) - assert old_default_group_row["default_group_for_meeting_id"] == self.meeting1_id + old_default_group_row = curs.execute( + *group_v.select( + group_v.default_group_for_meeting_id, + where=group_v.id == old_default_group_id, + ) + ).fetchone() + assert ( + old_default_group_row["default_group_for_meeting_id"] + == self.meeting1_id + ) # change default group with self.db_connection.transaction(): - group_staff_row = curs.execute(sql.SQL("SELECT id, name, meeting_id, default_group_for_meeting_id FROM group_ where name = %s and meeting_id = %s;"), ("Staff", self.meeting1_id)).fetchone() + group_staff_row = curs.execute( + *group_v.select( + group_v.id, + group_v.name, + group_v.meeting_id, + group_v.default_group_for_meeting_id, + where=( + (group_v.name == "Staff") + & (group_v.meeting_id == self.meeting1_id) + ), + ) + ).fetchone() assert group_staff_row["id"] == self.groupM1_staff_id assert group_staff_row["name"] == "Staff" assert group_staff_row["meeting_id"] == self.meeting1_id - assert group_staff_row["default_group_for_meeting_id"] == None - curs.execute(sql.SQL("UPDATE meeting_t SET default_group_id = %s where id = %s;"), (group_staff_row["id"], self.meeting1_id)) + assert group_staff_row["default_group_for_meeting_id"] is None + curs.execute( + *meeting_t.update( + [meeting_t.default_group_id], + [group_staff_row["id"]], + where=meeting_t.id == self.meeting1_id, + ) + ) + # assert new and old data - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["default_group_id"]) + meeting_row = curs.execute( + *meeting_v.select( + meeting_v.default_group_id, where=meeting_v.id == self.meeting1_id + ) + ).fetchone() assert meeting_row["default_group_id"] == group_staff_row["id"] - new_default_group_row = DbUtils.select_id_wrapper(curs, "group_", group_staff_row["id"], ["default_group_for_meeting_id"]) - assert new_default_group_row["default_group_for_meeting_id"] == self.meeting1_id - old_default_group_row = DbUtils.select_id_wrapper(curs, "group_", old_default_group_id, ["default_group_for_meeting_id"]) - assert old_default_group_row["default_group_for_meeting_id"] == None + new_default_group_row = curs.execute( + *group_v.select( + group_v.default_group_for_meeting_id, + where=group_v.id == group_staff_row["id"], + ) + ).fetchone() + assert ( + new_default_group_row["default_group_for_meeting_id"] + == self.meeting1_id + ) + old_default_group_row = curs.execute( + *group_v.select( + group_v.default_group_for_meeting_id, + where=group_v.id == old_default_group_id, + ) + ).fetchone() + assert old_default_group_row["default_group_for_meeting_id"] is None - """ 1:n relation tests with n-side NOT NULL """ - """ Test:motion_state.submitter_withdraw_back_ids: sql okay?""" - def test_one_to_many_1t_ntR_update_error(self) -> None: - """ update removes default projector => Exception""" - with self.db_connection.cursor() as curs: - with pytest.raises(psycopg.errors.RaiseException) as e: - projector_id = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone()['id'] - with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_id,)) - assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) - - def test_one_to_many_1t_ntR_update_okay(self) -> None: - """ Update sets new default projector before 2nd removes old default projector""" - with self.db_connection.cursor() as curs: - projector_ids = curs.execute("SELECT id from projector where meeting_id = %s", (self.meeting1_id,)).fetchall() - with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) - curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) - assert projector_ids[1]["id"] == DbUtils.select_id_wrapper(curs, 'meeting', self.meeting1_id, ['default_projector_topic_ids'])['default_projector_topic_ids'][0] - - def test_one_to_many_1t_ntR_update_wrong_update_sequence_error(self) -> None: - """ first update removes the projector from meeting => Exception""" - with self.db_connection.cursor() as curs: - projector_ids = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchall() - with pytest.raises(psycopg.errors.RaiseException) as e: - with self.db_connection.transaction(): - curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = null where id = %s;"), (projector_ids[0]["id"],)) - curs.execute(sql.SQL("UPDATE projector_t SET used_as_default_projector_for_topic_in_meeting_id = %s where id = %s;"), (self.meeting1_id, projector_ids[1]["id"])) - assert 'Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids' in str(e) - - def test_one_to_many_1t_ntR_delete_error(self) -> None: - """ delete projector from meeting => Exception""" - with self.db_connection.cursor() as curs: - projector_id = curs.execute("SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone()["id"] - with pytest.raises(psycopg.errors.RaiseException) as e: - with self.db_connection.transaction(): - curs.execute(sql.SQL("DELETE FROM projector where id = %s;"), (projector_id,)) - assert 'Exception: NOT NULL CONSTRAINT VIOLATED' in str(e) - - def test_one_to_many_1t_ntR_delete_insert_okay(self) -> None: - """ first insert, than delete old default projector from meeting => okay""" - with self.db_connection.cursor() as curs: - with self.db_connection.transaction(): - projector = curs.execute("SELECT * from projector where used_as_default_projector_for_topic_in_meeting_id = %s", (self.meeting1_id,)).fetchone() - field_list = ["meeting_id", "used_as_default_projector_for_agenda_item_list_in_meeting_id", "used_as_default_projector_for_topic_in_meeting_id", "used_as_default_projector_for_list_of_speakers_in_meeting_id", "used_as_default_projector_for_current_los_in_meeting_id", "used_as_default_projector_for_motion_in_meeting_id", "used_as_default_projector_for_amendment_in_meeting_id", "used_as_default_projector_for_motion_block_in_meeting_id", "used_as_default_projector_for_assignment_in_meeting_id", "used_as_default_projector_for_mediafile_in_meeting_id", "used_as_default_projector_for_message_in_meeting_id", "used_as_default_projector_for_countdown_in_meeting_id", "used_as_default_projector_for_assignment_poll_in_meeting_id", "used_as_default_projector_for_motion_poll_in_meeting_id", "used_as_default_projector_for_poll_in_meeting_id"] - data = {fname: projector[fname] for fname in field_list} - data["sequential_number"] = projector["sequential_number"] + 2 - new_projector_id = DbUtils.insert_wrapper(curs, "projector_t", data) - curs.execute(sql.SQL("UPDATE meeting_t SET reference_projector_id = %s where id = %s;"), (new_projector_id, projector["meeting_id"])) - curs.execute(sql.SQL("DELETE FROM projector where id = %s;"), (projector["id"],)) - assert new_projector_id == cast(dict, DbUtils.select_id_wrapper(curs, "meeting", projector["meeting_id"], ["default_projector_topic_ids"]))["default_projector_topic_ids"][0] - - """ n:m relation tests """ - """ manual sqls tests""" - """ all field type tests """ - """ constraint tests """ - - """ generic-relation tests """ - def test_generic_1GT_1tR(self) -> None: + # todo: 1tR:1Gr + def test_o2o_generic_1tR_1Gr_okay_with_setval(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): pcl_id = 2 option_id = 3 - curs.execute("select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", (pcl_id,)) - assert pcl_id == DbUtils.insert_wrapper(curs, "poll_candidate_list_t", {"id": pcl_id, "meeting_id": self.meeting1_id}) - curs.execute("select setval(pg_get_serial_sequence('option_t', 'id'), %s);", (option_id,)) - assert option_id == DbUtils.insert_wrapper(curs, "option_t", {"id": option_id, "content_object_id": (content_object_id := f"poll_candidate_list/{pcl_id}"), "meeting_id": self.meeting1_id}) - option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_poll_candidate_list_id", "content_object_id_user_id"]) + curs.execute( + "select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", + (pcl_id,), + ) + assert ( + pcl_id + == curs.execute( + *poll_candidate_list_t.insert( + [ + poll_candidate_list_t.id, + poll_candidate_list_t.meeting_id, + ], + [[pcl_id, self.meeting1_id]], + returning=[poll_candidate_list_t.id], + ) + ).fetchone()["id"] + ) + curs.execute( + "select setval(pg_get_serial_sequence('option_t', 'id'), %s);", + (option_id,), + ) + data = [ + { + "id": option_id, + "content_object_id": ( + content_object_id := f"poll_candidate_list/{pcl_id}" + ), + "meeting_id": self.meeting1_id, + }, + ] + columns, values = DbUtils.get_columns_and_values_for_insert( + option_t, data + ) + assert ( + option_id + == curs.execute( + *option_t.insert(columns, values, returning=[option_t.id]) + ).fetchone()["id"] + ) + columns = DbUtils.get_columns_from_list( + option_t, + [ + "id", + "content_object_id", + "content_object_id_poll_candidate_list_id", + "content_object_id_user_id", + ], + ) + option_row = curs.execute( + *option_t.select(*columns, where=option_t.id == option_id) + ).fetchone() assert option_row["id"] == option_id assert option_row["content_object_id"] == content_object_id assert option_row["content_object_id_poll_candidate_list_id"] == pcl_id - assert option_row["content_object_id_user_id"] == None + assert option_row["content_object_id_user_id"] is None - pcl_row = DbUtils.select_id_wrapper(curs, "poll_candidate_list", pcl_id, ["id", "option_id",]) + pcl_row = curs.execute( + *poll_candidate_list_v.select( + poll_candidate_list_v.id, + poll_candidate_list_v.option_id, + where=poll_candidate_list_v.id == pcl_id, + ) + ).fetchone() assert pcl_row["id"] == pcl_id assert pcl_row["option_id"] == option_id - def test_generic_1GT_nt(self) -> None: + # todo: 1tR:1GrR to implement R:R + def test_generic_1tR_1GrR_okay(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - option_id = 3 - curs.execute("select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", (option_id,)) - option_id == DbUtils.insert_wrapper(curs, "option_t", {"id": option_id, "content_object_id": (content_object_id := f"user/{self.user1_id}"), "meeting_id": self.meeting1_id}) - option_row = DbUtils.select_id_wrapper(curs, "option", option_id, ["id", "content_object_id", "content_object_id_user_id", "content_object_id_poll_candidate_list_id"]) - assert option_row["id"] == option_id - assert option_row["content_object_id"] == content_object_id - assert option_row["content_object_id_user_id"] == self.user1_id - assert option_row["content_object_id_poll_candidate_list_id"] == None + assignment_id = 2 + los_id = 3 + curs.execute( + "select setval(pg_get_serial_sequence('assignment_t', 'id'), %s);", + (assignment_id,), + ) + columns, values = DbUtils.get_columns_and_values_for_insert( + assignment_t, + [ + { + "id": assignment_id, + "title": "I am an assignment", + "sequential_number": 42, + "meeting_id": self.meeting1_id, + }, + ], + ) + assert ( + assignment_id + == curs.execute( + *assignment_t.insert( + columns, values, returning=[assignment_t.id] + ) + ).fetchone()["id"] + ) - user_row = DbUtils.select_id_wrapper(curs, "user_", self.user1_id, ["username", "option_ids",]) - assert user_row["option_ids"] == [option_id] - assert user_row["username"] == "admin" + curs.execute( + "select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", + (los_id,), + ) + columns, values = DbUtils.get_columns_and_values_for_insert( + list_of_speakers_t, + [ + { + "id": los_id, + "content_object_id": ( + content_object_id := f"assignment/{assignment_id}" + ), + "meeting_id": self.meeting1_id, + "sequential_number": 28, + } + ], + ) + assert ( + los_id + == curs.execute( + *list_of_speakers_t.insert( + columns, values, returning=[list_of_speakers_t.id] + ) + ).fetchone()["id"] + ) - def test_generic_1GTR_1t(self) -> None: - with self.db_connection.cursor() as curs: - with self.db_connection.transaction(): - mediafile_id = 2 - los_id = 3 - curs.execute("select setval(pg_get_serial_sequence('mediafile_t', 'id'), %s);", (mediafile_id,)) - assert mediafile_id == DbUtils.insert_wrapper(curs, "mediafile_t", {"id": mediafile_id, "is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}) - curs.execute("select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", (los_id,)) - assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakers_t", {"id": los_id, "content_object_id": (content_object_id := f"mediafile/{mediafile_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) - los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_mediafile_id", "content_object_id_topic_id"]) + los_row = curs.execute( + *list_of_speakers_v.select( + list_of_speakers_v.id, + list_of_speakers_v.content_object_id, + where=list_of_speakers_v.id == los_id, + ) + ).fetchone() assert los_row["id"] == los_id assert los_row["content_object_id"] == content_object_id - assert los_row["content_object_id_mediafile_id"] == mediafile_id - assert los_row["content_object_id_topic_id"] == None - mediafile_row = DbUtils.select_id_wrapper(curs, "mediafile", mediafile_id, ["id", "list_of_speakers_id", "owner_id"]) - assert mediafile_row["id"] == mediafile_id - assert mediafile_row["list_of_speakers_id"] == los_id + assignment_row = curs.execute( + *assignment_v.select( + assignment_v.id, + assignment_v.list_of_speakers_id, + where=assignment_v.id == assignment_id, + ) + ).fetchone() + assert assignment_row["id"] == assignment_id + assert assignment_row["list_of_speakers_id"] == los_id - def test_generic_1GTR_1tR(self) -> None: + # todo: nGt:nt only error check nGt + def test_generic_nGt_check_constraint_error(self) -> None: + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + tag_id = curs.execute( + *organization_tag_t.insert( + [organization_tag_t.name, organization_tag_t.color], + [["Orga Tag 1", "#ffee13"]], + returning=[organization_tag_t.id], + ) + ).fetchone()["id"] + columns, values = DbUtils.get_columns_and_values_for_insert( + gm_organization_tag_tagged_ids_t, + [ + { + "organization_tag_id": tag_id, + "tagged_id": f"committee/{self.committee1_id}", + }, + { + "organization_tag_id": tag_id, + "tagged_id": f"motion_state/{self.meeting1_id}", + }, + ], + ) + curs.execute( + *gm_organization_tag_tagged_ids_t.insert(columns, values) + ) + assert ( + 'new row for relation "gm_organization_tag_tagged_ids_t" violates check constraint "valid_tagged_id_part1"' + in str(e) + ) + + def test_generic_nGt_unique_constraint_error(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - assignment_id = 2 - los_id = 3 - curs.execute("select setval(pg_get_serial_sequence('assignment_t', 'id'), %s);", (assignment_id,)) - assert assignment_id == DbUtils.insert_wrapper(curs, "assignment_t", {"id": assignment_id, "title": "I am an assignment", "sequential_number": 42, "meeting_id": self.meeting1_id}) - curs.execute("select setval(pg_get_serial_sequence('list_of_speakers_t', 'id'), %s);", (los_id,)) - assert los_id == DbUtils.insert_wrapper(curs, "list_of_speakers_t", {"id": los_id, "content_object_id": (content_object_id := f"assignment/{assignment_id}"), "meeting_id": self.meeting1_id, "sequential_number": 28}) - los_row = DbUtils.select_id_wrapper(curs, "list_of_speakers", los_id, ["id", "content_object_id", "content_object_id_assignment_id", "content_object_id_topic_id"]) - assert los_row["id"] == los_id - assert los_row["content_object_id"] == content_object_id - assert los_row["content_object_id_assignment_id"] == assignment_id - assert los_row["content_object_id_topic_id"] == None + tag_id = curs.execute( + *organization_tag_t.insert( + [organization_tag_t.name, organization_tag_t.color], + [["Orga Tag 1", "#ffee13"]], + returning=[organization_tag_t.id], + ) + ).fetchone()["id"] + curs.execute( + *gm_organization_tag_tagged_ids_t.insert( + [ + gm_organization_tag_tagged_ids_t.organization_tag_id, + gm_organization_tag_tagged_ids_t.tagged_id, + ], + [[tag_id, f"committee/{self.committee1_id}"]], + returning=[gm_organization_tag_tagged_ids_t.id], + ) + ).fetchone()["id"] + with pytest.raises(psycopg.DatabaseError) as e: + with self.db_connection.transaction(): + curs.execute( + *gm_organization_tag_tagged_ids_t.insert( + [ + gm_organization_tag_tagged_ids_t.organization_tag_id, + gm_organization_tag_tagged_ids_t.tagged_id, + ], + [[tag_id, f"committee/{self.committee1_id}"]], + returning=[gm_organization_tag_tagged_ids_t.id], + ) + ) + assert "duplicate key value violates unique constraint" in str(e) - assignment_row = DbUtils.select_id_wrapper(curs, "assignment", assignment_id, ["id", "list_of_speakers_id"]) - assert assignment_row["id"] == assignment_id - assert assignment_row["list_of_speakers_id"] == los_id + # todo: nr + # todo: nt:1Gr + def test_generic_nt_1Gr(self) -> None: + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + option_id = 3 + curs.execute( + "select setval(pg_get_serial_sequence('poll_candidate_list_t', 'id'), %s);", + (option_id,), + ) + columns, values = DbUtils.get_columns_and_values_for_insert( + option_t, + [ + { + "id": option_id, + "content_object_id": ( + content_object_id := f"user/{self.user1_id}" + ), + "meeting_id": self.meeting1_id, + }, + ], + ) + option_id = curs.execute( + *option_t.insert(columns, values, returning=[option_t.id]) + ).fetchone()["id"] + option_row = curs.execute( + *option_t.select( + *DbUtils.get_columns_from_list( + option_t, + [ + "id", + "content_object_id", + "content_object_id_user_id", + "content_object_id_poll_candidate_list_id", + ], + ), + where=option_t.id == option_id, + ) + ).fetchone() + assert option_row["id"] == option_id + assert option_row["content_object_id"] == content_object_id + assert option_row["content_object_id_user_id"] == self.user1_id + assert option_row["content_object_id_poll_candidate_list_id"] is None - def test_generic_1GTR_nt(self) -> None: + user_row = curs.execute( + *user_v.select( + user_v.username, user_v.option_ids, where=user_v.id == self.user1_id + ) + ).fetchone() + assert user_row["option_ids"] == [option_id] + assert user_row["username"] == "admin" + + # todo: nt:1GrR + def test_o2m_generic_nt_1GrR_okay(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - DbUtils.insert_many_wrapper(curs, "mediafile_t", [ - { - "is_public": True, - "owner_id": f"meeting/{self.meeting1_id}" - }, - { - "is_public": True, - "owner_id": f"organization/{self.organization_id}" - }, - { - "is_public": True, - "owner_id": f"meeting/{self.meeting1_id}" - }, - { - "is_public": True, - "owner_id": f"organization/{self.organization_id}" - }, - ]) - rows = DbUtils.select_id_wrapper(curs, "mediafile", field_names=["owner_id", "owner_id_meeting_id", "owner_id_organization_id"]) - expected_results = (("meeting/1", 1, None), ("organization/1", None, 1), ("meeting/1", 1, None), ("organization/1", None, 1)) - for i, row in enumerate (rows): + columns, values = DbUtils.get_columns_and_values_for_insert( + mediafile_t, + [ + {"is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}, + { + "is_public": True, + "owner_id": f"organization/{self.organization_id}", + }, + {"is_public": True, "owner_id": f"meeting/{self.meeting1_id}"}, + { + "is_public": True, + "owner_id": f"organization/{self.organization_id}", + }, + ], + ) + curs.execute(*mediafile_t.insert(columns, values)) + rows = curs.execute( + *mediafile_v.select( + mediafile_v.owner_id, + mediafile_v.owner_id_meeting_id, + mediafile_v.owner_id_organization_id, + ) + ).fetchall() + expected_results = ( + ("meeting/1", 1, None), + ("organization/1", None, 1), + ("meeting/1", 1, None), + ("organization/1", None, 1), + ) + for i, row in enumerate(rows): assert tuple(row.values()) == expected_results[i] - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["mediafile_ids"]) + meeting_row = curs.execute( + *meeting_v.select( + meeting_v.mediafile_ids, where=meeting_v.id == self.meeting1_id + ) + ).fetchone() assert meeting_row["mediafile_ids"] == [1, 3] - organization_row = DbUtils.select_id_wrapper(curs, "organization", self.organization_id, ["mediafile_ids"]) + organization_row = curs.execute( + *organization_v.select(organization_v.mediafile_ids) + ).fetchone() assert organization_row["mediafile_ids"] == [2, 4] - def test_generic_1Gt_check_constraint_error(self) -> None: - with pytest.raises(psycopg.DatabaseError) as e: - with self.db_connection.cursor() as curs: - with self.db_connection.transaction(): - DbUtils.insert_wrapper(curs, "mediafile_t", { - "is_public": True, - "owner_id": f"motion_state/{self.meeting1_id}" - }) - assert 'motion_state/1' in str(e) - - """ generic-relation-list tests """ - def test_generic_nGt_nt(self) -> None: + # todo: nt:1r + # todo: nt:1rR + # todo: nt:nGt + def test_n2m_generic_nt_nGt_okay(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - tag_ids = DbUtils.insert_many_wrapper(curs, "organization_tag_t", [ - { - "name": "Orga Tag 1", - "color": "#ffee13" - }, - { - "name": "Orga Tag 2", - "color": "#12ee13" - }, - { - "name": "Orga Tag 3", - "color": "#00ee13" - }, - ]) - DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ - {"organization_tag_id": tag_ids[0], "tagged_id": f"committee/{self.committee1_id}"}, - {"organization_tag_id": tag_ids[0], "tagged_id": f"meeting/{self.meeting1_id}"}, - {"organization_tag_id": tag_ids[1], "tagged_id": f"committee/{self.committee1_id}"}, - {"organization_tag_id": tag_ids[2], "tagged_id": f"meeting/{self.meeting1_id}"}, - ]) - rows = DbUtils.select_id_wrapper(curs, "gm_organization_tag_tagged_ids_t", field_names=["id", "organization_tag_id", "tagged_id", "tagged_id_committee_id", "tagged_id_meeting_id"]) - expected_results = ((1, 1, "committee/1", 1, None), (2, 1, "meeting/1", None, 1), (3, 2, "committee/1", 1, None), (4, 3, "meeting/1", None, 1)) - for i, row in enumerate (rows): + columns, values = DbUtils.get_columns_and_values_for_insert( + organization_tag_t, + [ + {"name": "Orga Tag 1", "color": "#ffee13"}, + {"name": "Orga Tag 2", "color": "#12ee13"}, + {"name": "Orga Tag 3", "color": "#00ee13"}, + ], + ) + + curs.execute( + *organization_tag_t.insert( + columns, values, returning=[organization_tag_t.id] + ) + ) + tag_ids = [x["id"] for x in curs] + columns, values = DbUtils.get_columns_and_values_for_insert( + gm_organization_tag_tagged_ids_t, + [ + { + "organization_tag_id": tag_ids[0], + "tagged_id": f"committee/{self.committee1_id}", + }, + { + "organization_tag_id": tag_ids[0], + "tagged_id": f"meeting/{self.meeting1_id}", + }, + { + "organization_tag_id": tag_ids[1], + "tagged_id": f"committee/{self.committee1_id}", + }, + { + "organization_tag_id": tag_ids[2], + "tagged_id": f"meeting/{self.meeting1_id}", + }, + ], + ) + curs.execute(*gm_organization_tag_tagged_ids_t.insert(columns, values)) + rows = curs.execute( + *gm_organization_tag_tagged_ids_t.select( + *DbUtils.get_columns_from_list( + gm_organization_tag_tagged_ids_t, + [ + "id", + "organization_tag_id", + "tagged_id", + "tagged_id_committee_id", + "tagged_id_meeting_id", + ], + ) + ) + ).fetchall() + expected_results = ( + (1, 1, "committee/1", 1, None), + (2, 1, "meeting/1", None, 1), + (3, 2, "committee/1", 1, None), + (4, 3, "meeting/1", None, 1), + ) + for i, row in enumerate(rows): assert tuple(row.values()) == expected_results[i] - committee_row = DbUtils.select_id_wrapper(curs, "committee", self.committee1_id, ["organization_tag_ids"]) + committee_row = curs.execute( + *committee_v.select( + committee_v.organization_tag_ids, + where=committee_v.id == self.committee1_id, + ) + ).fetchone() assert committee_row["organization_tag_ids"] == [1, 2] - meeting_row = DbUtils.select_id_wrapper(curs, "meeting", self.meeting1_id, ["organization_tag_ids"]) + meeting_row = curs.execute( + *meeting_v.select( + meeting_v.organization_tag_ids, + where=meeting_v.id == self.meeting1_id, + ) + ).fetchone() assert meeting_row["organization_tag_ids"] == [1, 3] - def test_generic_nGt_check_constraint_error(self) -> None: - with pytest.raises(psycopg.DatabaseError) as e: - with self.db_connection.cursor() as curs: + # todo: nt:nt + # todo: ntR:1r + def test_o2m_ntR_1r_update_okay(self) -> None: + """Update sets new default projector before 2nd removes old default projector""" + with self.db_connection.cursor() as curs: + projector_ids = curs.execute( + *projector_t.select( + projector_t.id, where=projector_t.meeting_id == self.meeting1_id + ) + ).fetchall() + with self.db_connection.transaction(): + curs.execute( + *projector_t.update( + [projector_t.used_as_default_projector_for_topic_in_meeting_id], + [self.meeting1_id], + where=projector_t.id == [projector_ids[1]["id"]], + ) + ) + curs.execute( + *projector_t.update( + [projector_t.used_as_default_projector_for_topic_in_meeting_id], + [None], + where=projector_t.id == [projector_ids[0]["id"]], + ) + ) + assert ( + projector_ids[1]["id"] + == curs.execute( + *meeting_v.select( + meeting_v.default_projector_topic_ids, + where=meeting_v.id == self.meeting1_id, + ) + ).fetchone()["default_projector_topic_ids"][0] + ) + + def test_o2m_ntR_1r_update_error(self) -> None: + """update removes default projector => Exception""" + with self.db_connection.cursor() as curs: + with pytest.raises(psycopg.errors.RaiseException) as e: + projector_id = curs.execute( + *projector_t.select( + projector_t.id, + where=projector_t.used_as_default_projector_for_topic_in_meeting_id + == self.meeting1_id, + ) + ).fetchone()["id"] with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": "#ffee13"}) - DbUtils.insert_many_wrapper(curs, "gm_organization_tag_tagged_ids_t", [ - {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}, - {"organization_tag_id": tag_id, "tagged_id": f"motion_state/{self.meeting1_id}"}, - ]) - assert 'motion_state/1' in str(e) + curs.execute( + *projector_t.update( + [ + projector_t.used_as_default_projector_for_topic_in_meeting_id + ], + [ + None, + ], + where=projector_t.id == projector_id, + ) + ) + assert ( + "Exception: NOT NULL CONSTRAINT VIOLATED for meeting.default_projector_topic_ids" + in str(e) + ) - def test_generic_nGt_unique_constraint_error(self) -> None: + def test_o2m_ntR_1r_delete_error(self) -> None: + """delete projector from meeting => Exception""" with self.db_connection.cursor() as curs: - with self.db_connection.transaction(): - tag_id = DbUtils.insert_wrapper(curs, "organization_tag_t", {"name": "Orga Tag 1", "color": "#ffee13"}) - DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) - with pytest.raises(psycopg.DatabaseError) as e: + projector_id = curs.execute( + "SELECT id from projector where used_as_default_projector_for_topic_in_meeting_id = %s", + (self.meeting1_id,), + ).fetchone()["id"] + with pytest.raises(psycopg.errors.RaiseException) as e: with self.db_connection.transaction(): - DbUtils.insert_wrapper(curs, "gm_organization_tag_tagged_ids_t", {"organization_tag_id": tag_id, "tagged_id": f"committee/{self.committee1_id}"}) - assert 'duplicate key value violates unique constraint' in str(e) + curs.execute( + sql.SQL("DELETE FROM projector where id = %s;"), (projector_id,) + ) + assert "Exception: NOT NULL CONSTRAINT VIOLATED" in str(e) + + def test_o2m_ntR_1r_insert_delete_okay(self) -> None: + """first insert, than delete old default projector from meeting => okay""" + with self.db_connection.cursor() as curs: + with self.db_connection.transaction(): + columns = DbUtils.get_columns_from_list( + projector_t, + [ + "id", + "meeting_id", + "used_as_default_projector_for_agenda_item_list_in_meeting_id", + "used_as_default_projector_for_topic_in_meeting_id", + "used_as_default_projector_for_list_of_speakers_in_meeting_id", + "used_as_default_projector_for_current_los_in_meeting_id", + "used_as_default_projector_for_motion_in_meeting_id", + "used_as_default_projector_for_amendment_in_meeting_id", + "used_as_default_projector_for_motion_block_in_meeting_id", + "used_as_default_projector_for_assignment_in_meeting_id", + "used_as_default_projector_for_mediafile_in_meeting_id", + "used_as_default_projector_for_message_in_meeting_id", + "used_as_default_projector_for_countdown_in_meeting_id", + "used_as_default_projector_for_assignment_poll_in_meeting_id", + "used_as_default_projector_for_motion_poll_in_meeting_id", + "used_as_default_projector_for_poll_in_meeting_id", + "sequential_number", + ], + ) + projector = curs.execute( + *projector_t.select( + *columns, + where=projector_t.used_as_default_projector_for_topic_in_meeting_id + == self.meeting1_id, + ) + ).fetchone() + projector_old_id = projector.pop("id") + projector["sequential_number"] += 2 + columns, values = DbUtils.get_columns_and_values_for_insert( + projector_t, [projector] + ) + projector_new_id = curs.execute( + *projector_t.insert(columns, values, returning=[projector_t.id]) + ).fetchone()["id"] + curs.execute( + *meeting_t.update( + [meeting_t.reference_projector_id], [projector_new_id] + ) + ) + curs.execute( + *projector_t.delete(where=projector_t.id == projector_old_id) + ) + assert ( + projector_new_id + == curs.execute( + *meeting_v.select( + meeting_v.default_projector_topic_ids, + where=projector_new_id == meeting_v.reference_projector_id, + ) + ).fetchone()["default_projector_topic_ids"][0] + ) + + # todo: nts:nts + class EnumTests(BaseTestCase): def test_correct_singular_values_in_meeting(self) -> None: meeting_t = Table("meeting_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - meeting = curs.execute(*meeting_t.select(meeting_t.language, meeting_t.export_pdf_fontsize, where=meeting_t.id==1)).fetchone() + meeting = curs.execute( + *meeting_t.select( + meeting_t.language, + meeting_t.export_pdf_fontsize, + where=meeting_t.id == 1, + ) + ).fetchone() assert meeting["language"] == "en" assert meeting["export_pdf_fontsize"] == 10 - meeting = curs.execute(*meeting_t.update([meeting_t.language, meeting_t.export_pdf_fontsize], ["de", 11], where=meeting_t.id==1, returning=[meeting_t.id, meeting_t.language])).fetchone() + meeting = curs.execute( + *meeting_t.update( + [meeting_t.language, meeting_t.export_pdf_fontsize], + ["de", 11], + where=meeting_t.id == 1, + returning=[meeting_t.id, meeting_t.language], + ) + ).fetchone() assert meeting["language"] == "de" def test_wrong_language_in_meeting(self) -> None: @@ -315,7 +854,11 @@ def test_wrong_language_in_meeting(self) -> None: with self.db_connection.cursor() as curs: with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.transaction(): - curs.execute(*meeting_t.update([meeting_t.language], ["xx"], where=meeting_t.id==1)) + curs.execute( + *meeting_t.update( + [meeting_t.language], ["xx"], where=meeting_t.id == 1 + ) + ) assert 'violates check constraint "enum_meeting_language"' in str(e) def test_wrong_pdf_fontsize_in_meeting(self) -> None: @@ -323,20 +866,37 @@ def test_wrong_pdf_fontsize_in_meeting(self) -> None: with self.db_connection.cursor() as curs: with pytest.raises(psycopg.DatabaseError) as e: with self.db_connection.transaction(): - curs.execute(*meeting_t.update([meeting_t.export_pdf_fontsize], [22], where=meeting_t.id==1)) + curs.execute( + *meeting_t.update( + [meeting_t.export_pdf_fontsize], + [22], + where=meeting_t.id == 1, + ) + ) assert 'violates check constraint "enum_meeting_export_pdf_fontsize"' in str(e) def test_correct_permissions_in_group(self) -> None: group_t = Table("group_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - group = curs.execute(*group_t.select(group_t.permissions, where=group_t.id==1)).fetchone() + group = curs.execute( + *group_t.select(group_t.permissions, where=group_t.id == 1) + ).fetchone() assert "agenda_item.can_see_internal" in group["permissions"] assert "user.can_see" in group["permissions"] assert "chat.can_manage" not in group["permissions"] group["permissions"].remove("user.can_see") group["permissions"].append("chat.can_manage") - sql = tuple(group_t.update([group_t.permissions], [DbUtils.get_pg_array_for_cu(group["permissions"]),], where=group_t.id==1, returning=[group_t.permissions])) + sql = tuple( + group_t.update( + [group_t.permissions], + [ + DbUtils.get_pg_array_for_cu(group["permissions"]), + ], + where=group_t.id == 1, + returning=[group_t.permissions], + ) + ) group = curs.execute(*sql).fetchone() assert "agenda_item.can_see_internal" in group["permissions"] assert "user.can_see" not in group["permissions"] @@ -348,16 +908,32 @@ def test_wrong_permissions_in_group(self) -> None: with self.db_connection.transaction(): with pytest.raises(psycopg.DatabaseError) as e: group = {"permissions": ["user.can_see", "invalid permission"]} - sql = tuple(group_t.update([group_t.permissions], [DbUtils.get_pg_array_for_cu(group["permissions"]),], where=group_t.id==1, returning=[group_t.permissions])) + sql = tuple( + group_t.update( + [group_t.permissions], + [ + DbUtils.get_pg_array_for_cu(group["permissions"]), + ], + where=group_t.id == 1, + returning=[group_t.permissions], + ) + ) group = curs.execute(*sql).fetchone() assert 'violates check constraint "enum_group_permissions"' in str(e) + class DataTypeTests(BaseTestCase): def test_color_type_correct(self) -> None: orga_tag_t = Table("organization_tag_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - orga_tags = curs.execute(*orga_tag_t.insert(columns=[orga_tag_t.name, orga_tag_t.color], values=[['Foo', '#ff12cc'], ["Bar", "#1234AA"]], returning=[orga_tag_t.id, orga_tag_t.name, orga_tag_t.color])).fetchall() + orga_tags = curs.execute( + *orga_tag_t.insert( + columns=[orga_tag_t.name, orga_tag_t.color], + values=[["Foo", "#ff12cc"], ["Bar", "#1234AA"]], + returning=[orga_tag_t.id, orga_tag_t.name, orga_tag_t.color], + ) + ).fetchall() assert orga_tags[0] == {"id": 1, "name": "Foo", "color": "#ff12cc"} assert orga_tags[1] == {"id": 2, "name": "Bar", "color": "#1234AA"} @@ -366,15 +942,36 @@ def test_color_type_not_null_error(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): with pytest.raises(psycopg.DatabaseError) as e: - curs.execute(*orga_tag_t.insert(columns=[orga_tag_t.name, orga_tag_t.color], values=[['Foo', None]], returning=[orga_tag_t.id, orga_tag_t.name, orga_tag_t.color])).fetchone() - assert 'null value in column "color" of relation "organization_tag_t" violates not-null constraint' in str(e) + curs.execute( + *orga_tag_t.insert( + columns=[orga_tag_t.name, orga_tag_t.color], + values=[["Foo", None]], + returning=[ + orga_tag_t.id, + orga_tag_t.name, + orga_tag_t.color, + ], + ) + ).fetchone() + assert ( + 'null value in column "color" of relation "organization_tag_t" violates not-null constraint' + in str(e) + ) def test_color_type_null_correct(self) -> None: sl_t = Table("structure_level_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - sl_id = curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', None, 1]], returning=[sl_t.id])).fetchone()["id"] - structure_level = curs.execute(*sl_t.select(sl_t.id, sl_t.color, where=sl_t.id==sl_id)).fetchone() + sl_id = curs.execute( + *sl_t.insert( + columns=[sl_t.name, sl_t.color, sl_t.meeting_id], + values=[["Foo", None, 1]], + returning=[sl_t.id], + ) + ).fetchone()["id"] + structure_level = curs.execute( + *sl_t.select(sl_t.id, sl_t.color, where=sl_t.id == sl_id) + ).fetchone() assert structure_level == {"id": sl_id, "color": None} def test_color_type_empty_string_error(self) -> None: @@ -382,21 +979,53 @@ def test_color_type_empty_string_error(self) -> None: with self.db_connection.cursor() as curs: with self.db_connection.transaction(): with pytest.raises(psycopg.DatabaseError) as e: - curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', '', 1]], returning=[sl_t.id])).fetchone()["id"] - assert """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" in str(e) + curs.execute( + *sl_t.insert( + columns=[sl_t.name, sl_t.color, sl_t.meeting_id], + values=[["Foo", "", 1]], + returning=[sl_t.id], + ) + ).fetchone()["id"] + assert ( + """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" + in str(e) + ) def test_color_type_wrong_string_error(self) -> None: sl_t = Table("structure_level_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): with pytest.raises(psycopg.DatabaseError) as e: - curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', 'xxx', 1]], returning=[sl_t.id])).fetchone()["id"] - assert """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" in str(e) + curs.execute( + *sl_t.insert( + columns=[sl_t.name, sl_t.color, sl_t.meeting_id], + values=[["Foo", "xxx", 1]], + returning=[sl_t.id], + ) + ).fetchone()["id"] + assert ( + """new row for relation "structure_level_t" violates check constraint "structure_level_t_color_check""" + in str(e) + ) def test_color_type_to_long_string_error(self) -> None: sl_t = Table("structure_level_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): with pytest.raises(psycopg.DatabaseError) as e: - curs.execute(*sl_t.insert(columns=[sl_t.name, sl_t.color, sl_t.meeting_id], values=[['Foo', '#1234567', 1]], returning=[sl_t.id])).fetchone()["id"] + curs.execute( + *sl_t.insert( + columns=[sl_t.name, sl_t.color, sl_t.meeting_id], + values=[["Foo", "#1234567", 1]], + returning=[sl_t.id], + ) + ).fetchone()["id"] assert """value too long for type character varying(7)""" in str(e) + + +class ManualSqlTests(BaseTestCase): + pass + + +class ConstraintTests(BaseTestCase): + """foreign keys etc.""" diff --git a/models.yml b/models.yml index 1607a424..b957a8ee 100644 --- a/models.yml +++ b/models.yml @@ -66,7 +66,6 @@ # ("1t", "1rR"): (FieldSqlErrorType.SQL, False), # ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), # ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), -# ("1rR", "1t"): (FieldSqlErrorType.FIELD, False), # ("nGt", "nt"): (FieldSqlErrorType.SQL, True), # ("nr", ""): (FieldSqlErrorType.SQL, True), # ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), From 7203c19ebfa48d5c88c54fb37b16a725183ddc80 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 10:40:16 +0200 Subject: [PATCH 051/142] Fix models & rudimentary validation --- dev/src/validate.py | 3 +++ models.yml | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dev/src/validate.py b/dev/src/validate.py index ea3a42ab..145f94e8 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -52,6 +52,8 @@ "required", "read_only", "constant", + "unique", + "sql", ) @@ -201,6 +203,7 @@ def check_field( valid_attributes.append("equal_fields") if nested and type in ("relation", "relation-list"): valid_attributes.append("enum") + valid_attributes.extend(("reference", "deferred")) for attr in field.keys(): if attr not in valid_attributes: diff --git a/models.yml b/models.yml index 1607a424..ccdf1fdd 100644 --- a/models.yml +++ b/models.yml @@ -362,7 +362,7 @@ user: restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sqlTODO: >- + sql: >- # TODO (select array_agg(committee_id) from select m.committee_id as committee_id from group_to_user gtu join groupT g on g.id = gtu.group_id @@ -747,7 +747,7 @@ committee: restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sqlTODO: >- + sql: >- # TODO (select array_agg(user_id) from select gtu.user_id as user_id from meetingT m join groupT g on g.meeting_id = m.id @@ -1767,12 +1767,12 @@ meeting: restriction_mode: B logo_pdf_footer_l_id: type: relation - to: mediafile/used_as_logo_pdf_header_l_in_meeting_id + to: mediafile/used_as_logo_pdf_footer_l_in_meeting_id reference: mediafile restriction_mode: B logo_pdf_footer_r_id: type: relation - to: mediafile/used_as_logo_pdf_header_l_in_meeting_id + to: mediafile/used_as_logo_pdf_footer_r_in_meeting_id reference: mediafile restriction_mode: B logo_pdf_ballot_paper_id: @@ -1881,7 +1881,7 @@ meeting: to: projector/used_as_default_projector_for_list_of_speakers_in_meeting_id restriction_mode: B required: true - default_projector_current_list_of_speakers_ids: + default_projector_current_los_ids: type: relation-list to: projector/used_as_default_projector_for_current_los_in_meeting_id restriction_mode: B From ba658f8768baa025c7373868024ee5406eba751c Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 11:02:45 +0200 Subject: [PATCH 052/142] Re-enable CI --- .github/workflows/continuous_integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 5e6ba8ce..4a6f54bf 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -49,6 +49,6 @@ jobs: if: always() run: make pyupgrade - # - name: Validate models.yml - # if: always() - # run: make validate-models + - name: Validate models.yml + if: always() + run: make validate-models From 9cef28fa9cd9f0b637b1c580a8ce218d296ed2c0 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 15:42:47 +0200 Subject: [PATCH 053/142] Fix models --- dev/sql/schema_relational.sql | 58 +++++++++++++++++++---------------- dev/src/validate.py | 3 +- models.yml | 9 ++++-- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 766b941b..69b0fb06 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '959d3a581a8015a294d769587ebb1b6e' +-- MODELS_YML_CHECKSUM = '065e5f43ce5be122f236492eb9432b30' -- Type definitions -- Table definitions @@ -128,7 +128,6 @@ CREATE TABLE IF NOT EXISTS user_t ( is_demo_user boolean, last_login timestamptz, organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), - meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -136,7 +135,6 @@ CREATE TABLE IF NOT EXISTS user_t ( comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; -comment on column user_t.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; CREATE TABLE IF NOT EXISTS meeting_user_t ( @@ -405,7 +403,6 @@ This email was generated automatically.', font_projector_h1_id integer, font_projector_h2_id integer, committee_id integer NOT NULL, - user_ids integer[], reference_projector_id integer NOT NULL, list_of_speakers_countdown_id integer, poll_countdown_id integer, @@ -420,7 +417,6 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meeting_t.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; CREATE TABLE IF NOT EXISTS structure_level_t ( @@ -1109,12 +1105,6 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_user_ids_user_t ( - committee_id integer NOT NULL REFERENCES committee_t (id), - user_id integer NOT NULL REFERENCES user_t (id), - PRIMARY KEY (committee_id, user_id) -); - CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), @@ -1254,7 +1244,14 @@ FROM organization_t o; CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(n.committee_id) from nm_committee_user_ids_user_t n where n.user_id = u.id) as committee_ids, +(select array_agg(committee_id) from + select m.committee_id as committee_id from group_to_user gtu + join groupT g on g.id = gtu.group_id + join meetingT m on m.id = g.meeting_id + where gtu.user_id = u.id + union + select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id + ) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, @@ -1262,10 +1259,10 @@ CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, (select array_agg(v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, -(select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids +(select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, +todo FROM user_t u; -comment on column user_.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; CREATE OR REPLACE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, @@ -1294,14 +1291,20 @@ FROM theme_t t; CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -(select array_agg(n.user_id) from nm_committee_user_ids_user_t n where n.committee_id = c.id) as user_ids, +(select array_agg(user_id) from + select gtu.user_id as user_id from meetingT m + join groupT g on g.meeting_id = m.id + join group_to_user gtu on gtu.group_id = g.id + where m.committee_id = c.id + union + select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id +) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, (select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, (select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; -comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, @@ -1349,11 +1352,12 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_user_t n where n.meeting_id = m.id) as present_user_ids, +todo, (select array_agg(p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_list_of_speakers_ids, +(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_los_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, @@ -1809,12 +1813,12 @@ CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids A FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); --- definition trigger not null for meeting.default_projector_current_list_of_speakers_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_list_of_speakers_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); +-- definition trigger not null for meeting.default_projector_current_los_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_los_ids AFTER INSERT ON projector_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_list_of_speakers_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_los_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_ids against projector_t.used_as_default_projector_for_motion_in_meeting_id @@ -1928,7 +1932,7 @@ SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id SQL nr: => organization/user_ids:-> user/ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids -SQL nt:nt => user/committee_ids:-> committee/user_ids +SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1r => user/forwarding_committee_ids:-> committee/forwarding_user_id SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id @@ -1937,6 +1941,7 @@ SQL nt:1Gr => user/option_ids:-> option/content_object_id SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id +SQL nts:nts => user/meeting_ids:-> meeting/user_ids FIELD 1rR: => meeting_user/user_id:-> user/ FIELD 1rR: => meeting_user/meeting_id:-> meeting/ @@ -1959,7 +1964,7 @@ SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r: => committee/default_meeting_id:-> meeting/ -SQL nt:nt => committee/user_ids:-> user/committee_ids +SQL nts:nts => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids @@ -2034,6 +2039,7 @@ FIELD 1rR: => meeting/committee_id:-> committee/ SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids +SQL nts:nts => meeting/user_ids:-> user/meeting_ids FIELD 1rR: => meeting/reference_projector_id:-> projector/ FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ @@ -2041,7 +2047,7 @@ SQL nt:1GrR => meeting/projection_ids:-> projection/content_object_id SQL ntR:1r => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id SQL ntR:1r => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id SQL ntR:1r => meeting/default_projector_list_of_speakers_ids:-> projector/used_as_default_projector_for_list_of_speakers_in_meeting_id -SQL ntR:1r => meeting/default_projector_current_list_of_speakers_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id +SQL ntR:1r => meeting/default_projector_current_los_ids:-> projector/used_as_default_projector_for_current_los_in_meeting_id SQL ntR:1r => meeting/default_projector_motion_ids:-> projector/used_as_default_projector_for_motion_in_meeting_id SQL ntR:1r => meeting/default_projector_amendment_ids:-> projector/used_as_default_projector_for_amendment_in_meeting_id SQL ntR:1r => meeting/default_projector_motion_block_ids:-> projector/used_as_default_projector_for_motion_block_in_meeting_id @@ -2321,4 +2327,4 @@ There are 3 errors/warnings projection/content: type:JSON is marked as a calculated field and not generated in schema */ -/* Missing attribute handling for constant, on_delete, sqlTODO, sql, equal_fields, unique, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, on_delete, sql, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/dev/src/validate.py b/dev/src/validate.py index 145f94e8..84f1bdce 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -53,7 +53,6 @@ "read_only", "constant", "unique", - "sql", ) @@ -203,7 +202,7 @@ def check_field( valid_attributes.append("equal_fields") if nested and type in ("relation", "relation-list"): valid_attributes.append("enum") - valid_attributes.extend(("reference", "deferred")) + valid_attributes.extend(("reference", "deferred", "sql")) for attr in field.keys(): if attr not in valid_attributes: diff --git a/models.yml b/models.yml index ccdf1fdd..f3eed3e4 100644 --- a/models.yml +++ b/models.yml @@ -55,7 +55,7 @@ # r = reference, will result in a FIELD # to = to, collection with field get's an automatic sql in a view, if not together with `reference` # R = required -# The first tuple symbols the relation from-to, the 2nd FIELD or SQL and primary or not, only valis for lists +# The first tuple symbols the relation from-to, the 2nd FIELD or SQL and primary or not, only valid for lists # The original of the following list is used in source code as `decision_list` # ("1Gr", ""): (FieldSqlErrorType.FIELD, False), # ("1GrR", ""): (FieldSqlErrorType.FIELD, False), @@ -411,7 +411,8 @@ user: restriction_mode: A meeting_ids: - type: number[] + type: relation-list + to: meeting/user_ids description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true sql: todo @@ -1841,9 +1842,11 @@ meeting: to: user/is_present_in_meeting_ids restriction_mode: B user_ids: - type: number[] + type: relation-list + to: user/meeting_ids description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true + sql: todo restriction_mode: B reference_projector_id: type: relation From e509878445111592aabf600ca8fd0753218289f2 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 15:45:42 +0200 Subject: [PATCH 054/142] Comment out unfinished SQL statements --- dev/sql/schema_relational.sql | 50 ++++++++++++++++++----------------- models.yml | 40 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 69b0fb06..1253c5d1 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '065e5f43ce5be122f236492eb9432b30' +-- MODELS_YML_CHECKSUM = 'bbd75cac13c55c82f772ae52df63aa96' -- Type definitions -- Table definitions @@ -1105,6 +1105,12 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); +CREATE TABLE IF NOT EXISTS nm_committee_user_ids_user_t ( + committee_id integer NOT NULL REFERENCES committee_t (id), + user_id integer NOT NULL REFERENCES user_t (id), + PRIMARY KEY (committee_id, user_id) +); + CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), @@ -1123,6 +1129,12 @@ CREATE TABLE IF NOT EXISTS nm_meeting_present_user_ids_user_t ( PRIMARY KEY (meeting_id, user_id) ); +CREATE TABLE IF NOT EXISTS nm_meeting_user_ids_user_t ( + meeting_id integer NOT NULL REFERENCES meeting_t (id), + user_id integer NOT NULL REFERENCES user_t (id), + PRIMARY KEY (meeting_id, user_id) +); + CREATE TABLE IF NOT EXISTS nm_group_meeting_user_ids_meeting_user_t ( group_id integer NOT NULL REFERENCES group_t (id), meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), @@ -1244,14 +1256,7 @@ FROM organization_t o; CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(committee_id) from - select m.committee_id as committee_id from group_to_user gtu - join groupT g on g.id = gtu.group_id - join meetingT m on m.id = g.meeting_id - where gtu.user_id = u.id - union - select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id - ) as committee_ids, +(select array_agg(n.committee_id) from nm_committee_user_ids_user_t n where n.user_id = u.id) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, @@ -1260,9 +1265,11 @@ CREATE OR REPLACE VIEW user_ AS SELECT *, (select array_agg(v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, (select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, -todo +(select array_agg(n.meeting_id) from nm_meeting_user_ids_user_t n where n.user_id = u.id) as meeting_ids FROM user_t u; +comment on column user_.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column user_.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; CREATE OR REPLACE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, @@ -1291,20 +1298,14 @@ FROM theme_t t; CREATE OR REPLACE VIEW committee AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -(select array_agg(user_id) from - select gtu.user_id as user_id from meetingT m - join groupT g on g.meeting_id = m.id - join group_to_user gtu on gtu.group_id = g.id - where m.committee_id = c.id - union - select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id -) as user_ids, +(select array_agg(n.user_id) from nm_committee_user_ids_user_t n where n.committee_id = c.id) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, (select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, (select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; +comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; CREATE OR REPLACE VIEW meeting AS SELECT *, (select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, @@ -1352,7 +1353,7 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id) from nm_meeting_present_user_ids_user_t n where n.meeting_id = m.id) as present_user_ids, -todo, +(select array_agg(n.user_id) from nm_meeting_user_ids_user_t n where n.meeting_id = m.id) as user_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, @@ -1370,6 +1371,7 @@ todo, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids FROM meeting_t m; +comment on column meeting.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; CREATE OR REPLACE VIEW structure_level AS SELECT *, (select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, @@ -1932,7 +1934,7 @@ SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id SQL nr: => organization/user_ids:-> user/ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids -SQL nts:nts => user/committee_ids:-> committee/user_ids +SQL nt:nt => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1r => user/forwarding_committee_ids:-> committee/forwarding_user_id SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id @@ -1941,7 +1943,7 @@ SQL nt:1Gr => user/option_ids:-> option/content_object_id SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id -SQL nts:nts => user/meeting_ids:-> meeting/user_ids +SQL nt:nt => user/meeting_ids:-> meeting/user_ids FIELD 1rR: => meeting_user/user_id:-> user/ FIELD 1rR: => meeting_user/meeting_id:-> meeting/ @@ -1964,7 +1966,7 @@ SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r: => committee/default_meeting_id:-> meeting/ -SQL nts:nts => committee/user_ids:-> user/committee_ids +SQL nt:nt => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids @@ -2039,7 +2041,7 @@ FIELD 1rR: => meeting/committee_id:-> committee/ SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids -SQL nts:nts => meeting/user_ids:-> user/meeting_ids +SQL nt:nt => meeting/user_ids:-> user/meeting_ids FIELD 1rR: => meeting/reference_projector_id:-> projector/ FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ @@ -2327,4 +2329,4 @@ There are 3 errors/warnings projection/content: type:JSON is marked as a calculated field and not generated in schema */ -/* Missing attribute handling for constant, on_delete, sql, equal_fields, unique, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, on_delete, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/models.yml b/models.yml index f3eed3e4..adebeb71 100644 --- a/models.yml +++ b/models.yml @@ -362,15 +362,15 @@ user: restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sql: >- # TODO - (select array_agg(committee_id) from - select m.committee_id as committee_id from group_to_user gtu - join groupT g on g.id = gtu.group_id - join meetingT m on m.id = g.meeting_id - where gtu.user_id = u.id - union - select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id - ) as committee_ids + # sql: >- # TODO + # (select array_agg(committee_id) from + # select m.committee_id as committee_id from group_to_user gtu + # join groupT g on g.id = gtu.group_id + # join meetingT m on m.id = g.meeting_id + # where gtu.user_id = u.id + # union + # select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id + # ) as committee_ids # committee specific permissions committee_management_ids: @@ -415,7 +415,7 @@ user: to: meeting/user_ids description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true - sql: todo + # sql: todo restriction_mode: E organization_id: type: relation @@ -748,15 +748,15 @@ committee: restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sql: >- # TODO - (select array_agg(user_id) from - select gtu.user_id as user_id from meetingT m - join groupT g on g.meeting_id = m.id - join group_to_user gtu on gtu.group_id = g.id - where m.committee_id = c.id - union - select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id - ) as user_ids + # sql: >- # TODO + # (select array_agg(user_id) from + # select gtu.user_id as user_id from meetingT m + # join groupT g on g.meeting_id = m.id + # join group_to_user gtu on gtu.group_id = g.id + # where m.committee_id = c.id + # union + # select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id + # ) as user_ids manager_ids: type: relation-list to: user/committee_management_ids @@ -1846,7 +1846,7 @@ meeting: to: user/meeting_ids description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true - sql: todo + # sql: todo restriction_mode: B reference_projector_id: type: relation From 98307c57978810c411d3310c6b33a3379c1082ec Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 16:05:11 +0200 Subject: [PATCH 055/142] Revert relation change --- models.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/models.yml b/models.yml index adebeb71..cabb62d1 100644 --- a/models.yml +++ b/models.yml @@ -411,8 +411,7 @@ user: restriction_mode: A meeting_ids: - type: relation-list - to: meeting/user_ids + type: number[] description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true # sql: todo @@ -1842,8 +1841,7 @@ meeting: to: user/is_present_in_meeting_ids restriction_mode: B user_ids: - type: relation-list - to: user/meeting_ids + type: number[] description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true # sql: todo From 61a9e482ffd0aa38980b2fb35e2db1297a2c22b1 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 18 Apr 2024 16:47:54 +0200 Subject: [PATCH 056/142] fix hints from code review --- dev/setup.cfg | 9 +++++++++ dev/sql/schema_relational.sql | 2 +- dev/src/__init__.py | 0 dev/src/db_utils.py | 2 +- dev/src/python_sql.py | 2 ++ dev/tests/base.py | 8 ++++---- dev/tests/test_generic_relations.py | 10 ++++++---- 7 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 dev/src/__init__.py create mode 100644 dev/src/python_sql.py diff --git a/dev/setup.cfg b/dev/setup.cfg index 6575421d..c41105e1 100644 --- a/dev/setup.cfg +++ b/dev/setup.cfg @@ -17,3 +17,12 @@ extend-ignore = E203,E501 [mypy] disallow_untyped_defs = true + +[mypy-sql.*] +ignore_missing_imports = true + +[mypy-python_sql.*] +ignore_missing_imports = true + +[mypy-helper_get_names.*] +ignore_missing_imports = true \ No newline at end of file diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 766b941b..1fe13a4c 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '959d3a581a8015a294d769587ebb1b6e' +-- MODELS_YML_CHECKSUM = '385e6beb7945826015352886f2d0f97b' -- Type definitions -- Table definitions diff --git a/dev/src/__init__.py b/dev/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index 78633d29..3248561e 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -1,6 +1,6 @@ from typing import Any -from sql import Column, Table # type: ignore +from src.python_sql import Column, Table class DbUtils: diff --git a/dev/src/python_sql.py b/dev/src/python_sql.py new file mode 100644 index 00000000..97850a48 --- /dev/null +++ b/dev/src/python_sql.py @@ -0,0 +1,2 @@ +# imports from sql for usage with type: ignore, because mypy don'T recognize the original import +from sql import Column, Table # type: ignore # noqa:F401 diff --git a/dev/tests/base.py b/dev/tests/base.py index cd2aaaaa..482e7227 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -6,8 +6,8 @@ from psycopg import sql from psycopg.types.json import Jsonb -from sql import Table from src.db_utils import DbUtils +from src.python_sql import Table # ADMIN_USERNAME = "admin" # ADMIN_PASSWORD = "admin" @@ -16,7 +16,7 @@ class BaseTestCase(TestCase): temporary_template_db = "openslides_template" work_on_test_db = "openslides_test" - db_connection: psycopg.Connection = None + db_connection: psycopg.Connection # id's of pre loaded rows, see method populate_database meeting1_id = 0 @@ -49,7 +49,7 @@ def set_db_connection( raise Exception(f"Cannot connect to postgres: {e.message}") @classmethod - def setup_class(cls): + def setup_class(cls) -> None: env = os.environ cls.set_db_connection("postgres", True) with cls.db_connection: @@ -76,7 +76,7 @@ def setup_class(cls): cls.populate_database() @classmethod - def teardown_class(cls): + def teardown_class(cls) -> None: """remove last test db and drop the temporary template db""" cls.set_db_connection("postgres", True) with cls.db_connection: diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 7a3f67c3..1a0e6b08 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -1,9 +1,11 @@ +from typing import Any + import psycopg import pytest from psycopg import sql -from sql import Table from src.db_utils import DbUtils +from src.python_sql import Table from tests.base import BaseTestCase agenda_item_t = Table("agenda_item_t") @@ -160,7 +162,7 @@ def test_o2o_generic_1t_1GrR_okay_with_setval(self) -> None: ) ).fetchone()["id"] ) - los_row = curs.execute( + los_row: dict[str, Any] = curs.execute( *list_of_speakers_t.select( list_of_speakers_t.id, list_of_speakers_t.content_object_id, @@ -789,7 +791,7 @@ def test_o2m_ntR_1r_insert_delete_okay(self) -> None: "sequential_number", ], ) - projector = curs.execute( + projector: dict[str, Any] = curs.execute( *projector_t.select( *columns, where=projector_t.used_as_default_projector_for_topic_in_meeting_id @@ -879,7 +881,7 @@ def test_correct_permissions_in_group(self) -> None: group_t = Table("group_t") with self.db_connection.cursor() as curs: with self.db_connection.transaction(): - group = curs.execute( + group: dict[str, Any] = curs.execute( *group_t.select(group_t.permissions, where=group_t.id == 1) ).fetchone() assert "agenda_item.can_see_internal" in group["permissions"] From 7e253f4d792524a3992a0deb94ab1fa1cadbe6c8 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 11:53:28 +0200 Subject: [PATCH 057/142] Add support for self-referencing relations --- dev/sql/schema_relational.sql | 12 +++++++++--- dev/src/generate_sql_schema.py | 30 +++++++++++++++++++++--------- models.yml | 6 ++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 766b941b..5f3b5324 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '959d3a581a8015a294d769587ebb1b6e' +-- MODELS_YML_CHECKSUM = '2007f6054e0baa149f98b80cbe9ed670' -- Type definitions -- Table definitions @@ -612,7 +612,6 @@ CREATE TABLE IF NOT EXISTS motion_t ( sort_parent_id integer, origin_id integer, origin_meeting_id integer, - identical_motion_ids integer[], state_id integer NOT NULL, recommendation_id integer, category_id integer, @@ -625,7 +624,6 @@ CREATE TABLE IF NOT EXISTS motion_t ( comment on column motion_t.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column motion_t.identical_motion_ids is 'with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated.'; CREATE TABLE IF NOT EXISTS motion_submitter_t ( @@ -1186,6 +1184,12 @@ CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motion_t ( PRIMARY KEY (all_derived_motion_id, all_origin_id) ); +CREATE TABLE IF NOT EXISTS nm_motion_identical_motion_ids_motion_t ( + identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id), + identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id), + PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) +); + CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), @@ -1432,6 +1436,7 @@ CREATE OR REPLACE VIEW motion AS SELECT *, (select array_agg(m1.id) from motion_t m1 where m1.origin_id = m.id) as derived_motion_ids, (select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_derived_motion_id = m.id) as all_origin_ids, (select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_cat((select array_agg(n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, (select array_agg(g.id) from gm_motion_state_extension_reference_ids_t g where g.motion_id = m.id) as state_extension_reference_ids, (select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids_t g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, (select array_agg(g.id) from gm_motion_recommendation_extension_reference_ids_t g where g.motion_id = m.id) as recommendation_extension_reference_ids, @@ -2125,6 +2130,7 @@ FIELD 1r: => motion/origin_meeting_id:-> meeting/ SQL nt:1r => motion/derived_motion_ids:-> motion/origin_id SQL nt:nt => motion/all_origin_ids:-> motion/all_derived_motion_ids SQL nt:nt => motion/all_derived_motion_ids:-> motion/all_origin_ids +SQL nt:nt => motion/identical_motion_ids:-> motion/identical_motion_ids FIELD 1rR: => motion/state_id:-> motion_state/ FIELD 1r: => motion/recommendation_id:-> motion_state/ SQL nGt:nt => motion/state_extension_reference_ids:-> motion/referenced_in_motion_state_extension_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index a0e548c7..c55ff5fc 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -422,6 +422,7 @@ def get_relation_list_type( foreign_table_name, foreign_table_column, foreign_table_ref_column, + own_table_field.field_def == foreign_table_field.field_def, ) if comment := fdata.get("description"): text["post_view"] = Helper.get_post_view_comment( @@ -448,15 +449,25 @@ def get_sql_for_relation_n_1( foreign_table_name: str, foreign_table_column: str, foreign_table_ref_column: str, + self_reference: bool = False, ) -> str: table_letter = Helper.get_table_letter(table_name) - letters = [table_letter] - foreign_letter = Helper.get_table_letter(foreign_table_name, letters) + foreign_letter = Helper.get_table_letter(foreign_table_name, [table_letter]) foreign_table_name = HelperGetNames.get_table_name(foreign_table_name) - if foreign_table_column: - return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter} where {foreign_letter}.{foreign_table_column} = {table_letter}.{own_ref_column}) as {fname},\n" + AGG_TEMPLATE = f"select array_agg({foreign_letter}.{{}}) from {foreign_table_name} {foreign_letter}" + COND_TEMPLATE = ( + f" where {foreign_letter}.{{}} = {table_letter}.{own_ref_column}" + ) + if not foreign_table_column or not self_reference: + query = AGG_TEMPLATE.format(foreign_table_ref_column) + if foreign_table_column: + query += COND_TEMPLATE.format(foreign_table_column) else: - return f"(select array_agg({foreign_letter}.{foreign_table_ref_column}) from {foreign_table_name} {foreign_letter}) as {fname},\n" + assert foreign_table_ref_column == (col := foreign_table_column) + arr1 = AGG_TEMPLATE.format(f"{col}_1") + COND_TEMPLATE.format(f"{col}_2") + arr2 = AGG_TEMPLATE.format(f"{col}_2") + COND_TEMPLATE.format(f"{col}_1") + query = f"select array_cat(({arr1}), ({arr2}))" + return f"({query}) as {fname},\n" @classmethod def get_trigger_check_not_null_for_relation_lists( @@ -814,6 +825,9 @@ def get_nm_table_for_n_m_relation_lists( field2 = HelperGetNames.get_field_in_n_m_relation_list( foreign_table_field, own_table_field.table ) + if field1 == field2: + field1 += "_1" + field2 += "_2" text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute( { "table_name": HelperGetNames.get_table_name(nm_table_name), @@ -1082,11 +1096,9 @@ def generate_field_or_sql_decision( error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" state = FieldSqlErrorType.ERROR elif primary == "primary_decide_alphabetical": - if own.collectionfield == foreign.collectionfield: - error = f"Field {own.collectionfield} identical with foreign.collectionfield. SQL_decice_alphabetical uncedidable!\n" - state = FieldSqlErrorType.ERROR primary = ( - foreign.collectionfield == "-" + own.collectionfield == foreign.collectionfield + or foreign.collectionfield == "-" or own.collectionfield < foreign.collectionfield ) return cast(FieldSqlErrorType, state), cast(bool, primary), error diff --git a/models.yml b/models.yml index 1607a424..a52fe0e9 100644 --- a/models.yml +++ b/models.yml @@ -2690,10 +2690,8 @@ motion: to: motion/all_origin_ids restriction_mode: A identical_motion_ids: - type: number[] - # type: relation-list - # to: motion/identical_motion_ids - description: with psycopg 3.2.0 we could use the as_string method without cursor and change dummy to number. Changed from relation-list to number[], because it still can''t be generated. + type: relation-list + to: motion/identical_motion_ids restriction_mode: C state_id: type: relation From b3cfeb6d8137d9714762988c37340fd2bd599695 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 18 Apr 2024 17:39:18 +0200 Subject: [PATCH 058/142] fix hints from code review 2 --- dev/setup.cfg | 11 +---------- dev/src/db_utils.py | 2 +- dev/src/python_sql.py | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/dev/setup.cfg b/dev/setup.cfg index c41105e1..e27cfd7a 100644 --- a/dev/setup.cfg +++ b/dev/setup.cfg @@ -16,13 +16,4 @@ line_length = 88 extend-ignore = E203,E501 [mypy] -disallow_untyped_defs = true - -[mypy-sql.*] -ignore_missing_imports = true - -[mypy-python_sql.*] -ignore_missing_imports = true - -[mypy-helper_get_names.*] -ignore_missing_imports = true \ No newline at end of file +disallow_untyped_defs = true \ No newline at end of file diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index 3248561e..01de5ad7 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -1,6 +1,6 @@ from typing import Any -from src.python_sql import Column, Table +from .python_sql import Column, Table class DbUtils: diff --git a/dev/src/python_sql.py b/dev/src/python_sql.py index 97850a48..c94d3ace 100644 --- a/dev/src/python_sql.py +++ b/dev/src/python_sql.py @@ -1,2 +1,2 @@ -# imports from sql for usage with type: ignore, because mypy don'T recognize the original import +# mypy does not recognize the imports from `python-sql` correctly. Therefore, we gather them in this file so they can be imported from here in other places without using `type: ignore` everytime. from sql import Column, Table # type: ignore # noqa:F401 From 6da629caf87c3d98d5b2148f4f6ad3458f642e1c Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 19 Apr 2024 10:18:07 +0200 Subject: [PATCH 059/142] fix import helper_get_names --- dev/src/generate_sql_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index cb303c54..393a1f1b 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -9,7 +9,7 @@ from textwrap import dedent from typing import Any, TypedDict, cast -from helper_get_names import ( +from .helper_get_names import ( KEYSEPARATOR, HelperGetNames, InternalHelper, From 9077385ec4e4743e4cc88ea78d31752d827244c8 Mon Sep 17 00:00:00 2001 From: Danny Date: Fri, 17 May 2024 14:45:16 +0200 Subject: [PATCH 060/142] Ch attribute set is_view_field --- dev/src/generate_sql_schema.py | 117 ++-------------------------- dev/src/helper_get_names.py | 137 ++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 115 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 393a1f1b..302ad033 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -13,7 +13,9 @@ KEYSEPARATOR, HelperGetNames, InternalHelper, + GenerateHelper, TableFieldType, + FieldSqlErrorType, ) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() @@ -50,12 +52,6 @@ class SQL_Delete_Update_Options(str, Enum): NO_ACTION = "NO ACTION" -class FieldSqlErrorType(Enum): - FIELD = 1 - SQL = 2 - ERROR = 3 - - class SubstDict(TypedDict, total=False): """dict for substitutions of field templates""" @@ -944,59 +940,6 @@ def get_initials( def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: return f"comment on column {entity_name}.{fname} is '{comment}';\n" - @staticmethod - def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: - """ - Returns - - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) - - string with error message or empty string if no error - """ - error = "" - field = field_all.field_def - if field: - required = bool(field.get("required")) - sql = "sql" in field - to = bool(field.get("to")) - reference = bool(field.get("reference")) - - # general rules of inconsistent field descriptions on field level - if reference and not to: # temporaray rule to keep all to-attributes - error = "Field with reference temporarely needs also to-attribute\n" - elif field.get("sql") == "": - error = "sql attribute may not be empty\n" - elif required and sql: - error = "Field with attribute sql cannot be required\n" - elif not (to or reference): - error = "Relation field must have `to` or `reference` attribut set\n" - elif field["type"] == "generic-relation-list" and required: - error = "generic-relation-list cannot be required: not implemented\n" - - if field["type"] == "relation": - result = "1" - elif field["type"] == "relation-list": - result = "n" - elif field["type"] == "generic-relation": - result = "1G" - elif field["type"] == "generic-relation-list": - result = "nG" - else: - raise Exception( - f"Not implemented type {field['type']} in method get_cardinality found!" - ) - if reference: - result += "r" - if ( - to and not reference - ): # to with reference only for temporaray backup compatibility in backend relation-handling - result += "t" - if required: - result += "R" - if sql: - result += "s" - else: - result = "" - return result, error - @staticmethod def check_relation_definitions( own_field: TableFieldType, foreign_fields: list[TableFieldType] @@ -1017,12 +960,12 @@ def check_relation_definitions( - error line if error else empty string """ error = "" - own_c, tmp_error = Helper.get_cardinality(own_field) + own_c, tmp_error = InternalHelper.get_cardinality(own_field) error = error or tmp_error foreigns_c = [] foreign_collectionfields = [] for foreign_field in foreign_fields: - foreign_c, tmp_error = Helper.get_cardinality(foreign_field) + foreign_c, tmp_error = InternalHelper.get_cardinality(foreign_field) foreigns_c.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) @@ -1033,11 +976,11 @@ def check_relation_definitions( else: for i, foreign_field in enumerate(foreign_fields): if i == 0: - state, primary, error = Helper.generate_field_or_sql_decision( + state, primary, error = InternalHelper.generate_field_or_sql_decision( own_field, own_c, foreign_field, foreigns_c[i] ) else: - statex, primaryx, error = Helper.generate_field_or_sql_decision( + statex, primaryx, error = InternalHelper.generate_field_or_sql_decision( own_field, own_c, foreign_field, foreigns_c[i] ) if not error and (statex != state or primaryx != primary): @@ -1052,55 +995,7 @@ def check_relation_definitions( text += f" {error}" return state, primary, text, error - @staticmethod - def generate_field_or_sql_decision( - own: TableFieldType, own_c: str, foreign: TableFieldType, foreign_c: str - ) -> tuple[FieldSqlErrorType, bool, str]: - """ - Returns: - - field, sql, error for own => enum FieldSqlErrorType - - primary field for own: (only relevant for list fields) - - error line if error else empty string - """ - decision_list: dict[ - tuple[str, str], tuple[FieldSqlErrorType | None, bool | str | None] - ] = { - ("1Gr", ""): (FieldSqlErrorType.FIELD, False), - ("1GrR", ""): (FieldSqlErrorType.FIELD, False), - ("1r", ""): (FieldSqlErrorType.FIELD, False), - ("1rR", ""): (FieldSqlErrorType.FIELD, False), - ("1t", "1GrR"): (FieldSqlErrorType.SQL, False), - ("1t", "1r"): (FieldSqlErrorType.SQL, False), - ("1t", "1rR"): (FieldSqlErrorType.SQL, False), - ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), - ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), - ("nGt", "nt"): (FieldSqlErrorType.SQL, True), - ("nr", ""): (FieldSqlErrorType.SQL, True), - ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), - ("nt", "1GrR"): (FieldSqlErrorType.SQL, False), - ("nt", "1r"): (FieldSqlErrorType.SQL, False), - ("nt", "1rR"): (FieldSqlErrorType.SQL, False), - ("nt", "nGt"): (FieldSqlErrorType.SQL, False), - ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), - ("ntR", "1r"): (FieldSqlErrorType.SQL, False), - ("nts", "nts"): (FieldSqlErrorType.SQL, False), - } - - state: FieldSqlErrorType | str | None - primary: bool | str | None - error = "" - state, primary = decision_list.get((own_c, foreign_c), (None, None)) - if state is None: - error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" - state = FieldSqlErrorType.ERROR - elif primary == "primary_decide_alphabetical": - primary = ( - own.collectionfield == foreign.collectionfield - or foreign.collectionfield == "-" - or own.collectionfield < foreign.collectionfield - ) - return cast(FieldSqlErrorType, state), cast(bool, primary), error @staticmethod def get_generic_combined_fields( diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index dbc94364..6f2d183d 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -2,7 +2,8 @@ import os import re from collections.abc import Callable -from typing import Any +from typing import Any, cast +from enum import Enum import requests import yaml @@ -38,14 +39,21 @@ def get_definitions_from_foreign( fname = "" tfield: dict[str, Any] = {} ref_column = "" - if reference: - tname, ref_column = InternalHelper.get_foreign_key_table_column(reference) - elif to: + if to: tname, fname, tfield = InternalHelper.get_field_definition_from_to(to) ref_column = "id" + if reference: + tname, ref_column = InternalHelper.get_foreign_key_table_column(reference) + return TableFieldType(tname, fname, tfield, ref_column) +class FieldSqlErrorType(Enum): + FIELD = 1 + SQL = 2 + ERROR = 3 + + class HelperGetNames: MAX_LEN = 63 trigger_unique_list: list[str] = [] @@ -267,3 +275,124 @@ def get_models(cls, collection: str, field: str) -> dict[str, Any]: except KeyError: raise Exception(f"MODELS field {collection}.{field} doesn't exist") raise Exception("You have to initialize models in class InternalHelper") + + @staticmethod + def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: + """ + Returns + - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) + - string with error message or empty string if no error + """ + error = "" + field = field_all.field_def + if field: + required = bool(field.get("required")) + sql = "sql" in field + to = bool(field.get("to")) + reference = bool(field.get("reference")) + + # general rules of inconsistent field descriptions on field level + if reference and not to: # temporaray rule to keep all to-attributes + error = "Field with reference temporarely needs also to-attribute\n" + elif field.get("sql") == "": + error = "sql attribute may not be empty\n" + elif required and sql: + error = "Field with attribute sql cannot be required\n" + elif not (to or reference): + error = "Relation field must have `to` or `reference` attribut set\n" + elif field["type"] == "generic-relation-list" and required: + error = "generic-relation-list cannot be required: not implemented\n" + + if field["type"] == "relation": + result = "1" + elif field["type"] == "relation-list": + result = "n" + elif field["type"] == "generic-relation": + result = "1G" + elif field["type"] == "generic-relation-list": + result = "nG" + else: + raise Exception( + f"Not implemented type {field['type']} in method get_cardinality found!" + ) + if reference: + result += "r" + if ( + to and not reference + ): # to with reference only for temporaray backup compatibility in backend relation-handling + result += "t" + if required: + result += "R" + if sql: + result += "s" + else: + result = "" + return result, error + + @staticmethod + def generate_field_or_sql_decision( + own: TableFieldType, own_c: str, foreign: TableFieldType, foreign_c: str + ) -> tuple[FieldSqlErrorType, bool, str]: + """ + Returns: + - field, sql, error for own => enum FieldSqlErrorType + - primary field for own: (only relevant for list fields) + - error line if error else empty string + """ + decision_list: dict[ + tuple[str, str], tuple[FieldSqlErrorType | None, bool | str | None] + ] = { + ("1Gr", ""): (FieldSqlErrorType.FIELD, False), + ("1GrR", ""): (FieldSqlErrorType.FIELD, False), + ("1r", ""): (FieldSqlErrorType.FIELD, False), + ("1rR", ""): (FieldSqlErrorType.FIELD, False), + ("1t", "1GrR"): (FieldSqlErrorType.SQL, False), + ("1t", "1r"): (FieldSqlErrorType.SQL, False), + ("1t", "1rR"): (FieldSqlErrorType.SQL, False), + ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), + ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), + ("nGt", "nt"): (FieldSqlErrorType.SQL, True), + ("nr", ""): (FieldSqlErrorType.SQL, True), + ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), + ("nt", "1GrR"): (FieldSqlErrorType.SQL, False), + ("nt", "1r"): (FieldSqlErrorType.SQL, False), + ("nt", "1rR"): (FieldSqlErrorType.SQL, False), + ("nt", "nGt"): (FieldSqlErrorType.SQL, False), + ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), + ("ntR", "1r"): (FieldSqlErrorType.SQL, False), + ("nts", "nts"): (FieldSqlErrorType.SQL, False), + } + + state: FieldSqlErrorType | str | None + primary: bool | str | None + error = "" + + state, primary = decision_list.get((own_c, foreign_c), (None, None)) + if state is None: + error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" + state = FieldSqlErrorType.ERROR + elif primary == "primary_decide_alphabetical": + primary = ( + own.collectionfield == foreign.collectionfield + or foreign.collectionfield == "-" + or own.collectionfield < foreign.collectionfield + ) + return cast(FieldSqlErrorType, state), cast(bool, primary), error + + @staticmethod + def get_view_field_decision(collection_name: str, field_name: str, field_def: dict[str, any]) -> bool: + own : TableFieldType + foreign : TableFieldType + own_c : str + foreign_c : str + error = "" + + own = TableFieldType(HelperGetNames.get_table_name(collection_name), field_name, field_def) + own_c, error = InternalHelper.get_cardinality(own) + + foreign = TableFieldType.get_definitions_from_foreign(field_def.get("to", None), field_def.get("reference", None)) + foreign_c, error = InternalHelper.get_cardinality(foreign) + + field, primary, error = InternalHelper.generate_field_or_sql_decision(own, own_c, foreign, foreign_c) + + return field == FieldSqlErrorType.SQL From 7d6ccb1094c386d62e5390da8f94c00ae4806e13 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 5 Jun 2024 09:00:26 +0200 Subject: [PATCH 061/142] implemented get view field and write field method --- dev/src/generate_sql_schema.py | 112 ++------------------ dev/src/helper_get_names.py | 186 ++++++++++++++++++++++++++++++--- 2 files changed, 181 insertions(+), 117 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 302ad033..3300fe85 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -37,13 +37,6 @@ class SchemaZoneTexts(TypedDict, total=False): errors: list[str] -class ToDict(TypedDict): - """Defines the dict keys for the to-Attribute of generic relations in field definitions""" - - collections: list[str] - field: str - - class SQL_Delete_Update_Options(str, Enum): RESTRICT = "RESTRICT" CASCADE = "CASCADE" @@ -273,7 +266,7 @@ def get_relation_type( fdata.get("to"), fdata.get("reference") ) ) - state, _, final_info, error = Helper.check_relation_definitions( + state, _, final_info, error = InternalHelper.check_relation_definitions( own_table_field, [foreign_table_field] ) @@ -344,7 +337,7 @@ def get_relation_list_type( fdata.get("reference"), ) ) - state, primary, final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = InternalHelper.check_relation_definitions( own_table_field, [foreign_table_field] ) @@ -489,12 +482,12 @@ def get_generic_relation_type( text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_fields: list[TableFieldType] = ( - ModelsHelper.get_definitions_from_foreign_list( - table_name, fname, fdata.get("to"), fdata.get("reference") + InternalHelper.get_definitions_from_foreign_list( + fdata.get("to"), fdata.get("reference") ) ) - state, _, final_info, error = Helper.check_relation_definitions( + state, _, final_info, error = InternalHelper.check_relation_definitions( own_table_field, foreign_table_fields ) @@ -539,11 +532,11 @@ def get_generic_relation_list_type( text = cast(SchemaZoneTexts, defaultdict(str)) own_table_field = TableFieldType(table_name, fname, fdata) foreign_table_fields: list[TableFieldType] = ( - ModelsHelper.get_definitions_from_foreign_list( - table_name, fname, fdata.get("to"), fdata.get("reference") + InternalHelper.get_definitions_from_foreign_list( + fdata.get("to"), fdata.get("reference") ) ) - state, primary, final_info, error = Helper.check_relation_definitions( + state, primary, final_info, error = InternalHelper.check_relation_definitions( own_table_field, foreign_table_fields ) @@ -940,61 +933,6 @@ def get_initials( def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: return f"comment on column {entity_name}.{fname} is '{comment}';\n" - @staticmethod - def check_relation_definitions( - own_field: TableFieldType, foreign_fields: list[TableFieldType] - ) -> tuple[FieldSqlErrorType, bool, str, str]: - """ - Decides for the own-field, - - whether it is a field, a sql-expression or is there an error - - relation-list and generic-relation-list are always sql-expressions. - True significates, that it is the pimary that creates the intermediate table - - Also checks relational behaviour and produces the informative relation line and in - case of an error an error text - - Returns: - - field, sql, error => enum FieldSqlErrorType - - primary field (only relevant for list fields) - - complete relational text with FIELD, SQL or *** in front - - error line if error else empty string - """ - error = "" - own_c, tmp_error = InternalHelper.get_cardinality(own_field) - error = error or tmp_error - foreigns_c = [] - foreign_collectionfields = [] - for foreign_field in foreign_fields: - foreign_c, tmp_error = InternalHelper.get_cardinality(foreign_field) - foreigns_c.append(foreign_c) - error = error or tmp_error - foreign_collectionfields.append(foreign_field.collectionfield) - - if error: - state = FieldSqlErrorType.ERROR - primary = False - else: - for i, foreign_field in enumerate(foreign_fields): - if i == 0: - state, primary, error = InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] - ) - else: - statex, primaryx, error = InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] - ) - if not error and (statex != state or primaryx != primary): - error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" - if error: - state = FieldSqlErrorType.ERROR - break - - state_text = "***" if state == FieldSqlErrorType.ERROR else state.name - text = f"{state_text} {own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" - if state == FieldSqlErrorType.ERROR: - text += f" {error}" - return state, primary, text, error - @staticmethod @@ -1050,40 +988,6 @@ def get_foreign_table_from_to_or_reference( else: raise Exception("Relation field without reference or to") - @staticmethod - def get_definitions_from_foreign_list( - table: str, - field: str, - to: ToDict | list[str] | None, - reference: list[str] | None, - ) -> list[TableFieldType]: - """ - used for generic_relation with multiple foreign relations - """ - # temporarely allowed - # if to and reference: - # raise Exception( - # f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" - # ) - results: list[TableFieldType] = [] - # precedence for reference - if reference: - for ref in reference: - results.append(TableFieldType.get_definitions_from_foreign(None, ref)) - elif isinstance(to, dict): - fname = "/" + to["field"] - for table in to["collections"]: - results.append( - TableFieldType.get_definitions_from_foreign(table + fname, None) - ) - elif isinstance(to, list): - for collectionfield in to: - results.append( - TableFieldType.get_definitions_from_foreign(collectionfield, None) - ) - return results - - FIELD_TYPES: dict[str, dict[str, Any]] = { "string": { "pg_type": string.Template("varchar(${maxLength})"), diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 6f2d183d..e1c634c1 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -2,7 +2,7 @@ import os import re from collections.abc import Callable -from typing import Any, cast +from typing import Any, cast, TypedDict from enum import Enum import requests @@ -47,6 +47,11 @@ def get_definitions_from_foreign( return TableFieldType(tname, fname, tfield, ref_column) +class ToDict(TypedDict): + """Defines the dict keys for the to-Attribute of generic relations in field definitions""" + + collections: list[str] + field: str class FieldSqlErrorType(Enum): FIELD = 1 @@ -363,10 +368,21 @@ def generate_field_or_sql_decision( ("nts", "nts"): (FieldSqlErrorType.SQL, False), } + foreign_c_replacement_list: list[str] = [ + "1Gr", + "1GrR", + "1r", + "1rR", + "nr", + ] + state: FieldSqlErrorType | str | None primary: bool | str | None error = "" + if own_c in foreign_c_replacement_list: + foreign_c = "" + state, primary = decision_list.get((own_c, foreign_c), (None, None)) if state is None: error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" @@ -380,19 +396,163 @@ def generate_field_or_sql_decision( return cast(FieldSqlErrorType, state), cast(bool, primary), error @staticmethod - def get_view_field_decision(collection_name: str, field_name: str, field_def: dict[str, any]) -> bool: - own : TableFieldType - foreign : TableFieldType - own_c : str - foreign_c : str - error = "" + def check_relation_definitions( + own_field: TableFieldType, foreign_fields: list[TableFieldType] + ) -> tuple[FieldSqlErrorType, bool, str, str]: + """ + Decides for the own-field, + - whether it is a field, a sql-expression or is there an error + - relation-list and generic-relation-list are always sql-expressions. + True significates, that it is the pimary that creates the intermediate table - own = TableFieldType(HelperGetNames.get_table_name(collection_name), field_name, field_def) - own_c, error = InternalHelper.get_cardinality(own) + Also checks relational behaviour and produces the informative relation line and in + case of an error an error text + + Returns: + - field, sql, error => enum FieldSqlErrorType + - primary field (only relevant for list fields) + - complete relational text with FIELD, SQL or *** in front + - error line if error else empty string + """ + error = "" + own_c, tmp_error = InternalHelper.get_cardinality(own_field) + error = error or tmp_error + foreigns_c = [] + foreign_collectionfields = [] + for foreign_field in foreign_fields: + foreign_c, tmp_error = InternalHelper.get_cardinality(foreign_field) + foreigns_c.append(foreign_c) + error = error or tmp_error + foreign_collectionfields.append(foreign_field.collectionfield) + + if error: + state = FieldSqlErrorType.ERROR + primary = False + else: + for i, foreign_field in enumerate(foreign_fields): + if i == 0: + state, primary, error = InternalHelper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) + else: + statex, primaryx, error = InternalHelper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) + if not error and (statex != state or primaryx != primary): + error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" + if error: + state = FieldSqlErrorType.ERROR + break + + state_text = "***" if state == FieldSqlErrorType.ERROR else state.name + text = f"{state_text} {own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" + if state == FieldSqlErrorType.ERROR: + text += f" {error}" + return state, primary, text, error - foreign = TableFieldType.get_definitions_from_foreign(field_def.get("to", None), field_def.get("reference", None)) - foreign_c, error = InternalHelper.get_cardinality(foreign) + @staticmethod + def get_definitions_from_foreign_list( + to: ToDict | list[str] | None, + reference: list[str] | None, + ) -> list[TableFieldType]: + """ + used for generic_relation with multiple foreign relations + """ + # temporarely allowed + # if to and reference: + # raise Exception( + # f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" + # ) + results: list[TableFieldType] = [] + # precedence for reference + if reference: + for ref in reference: + results.append(TableFieldType.get_definitions_from_foreign(None, ref)) + elif isinstance(to, dict): + fname = "/" + to["field"] + for table in to["collections"]: + results.append( + TableFieldType.get_definitions_from_foreign(table + fname, None) + ) + elif isinstance(to, list): + for collectionfield in to: + results.append( + TableFieldType.get_definitions_from_foreign(collectionfield, None) + ) + else: + results.append(TableFieldType.get_definitions_from_foreign(to, None)) + return results - field, primary, error = InternalHelper.generate_field_or_sql_decision(own, own_c, foreign, foreign_c) + @staticmethod + def get_view_field_state_write_fields( + collection_name: str, field_name: str, value: dict[str, any] + ) -> (bool, tuple[str, str, str]): + """ + Purpose: + Checks whether a field is a view field and if other fields need to be written in an intermediate + table. + Input: + - collection_name + - field_name + - value : represents the definition of the field ( field_name in collection_name ) + Returns: + - is_view_field : whether the field is a view field or not + - write_fields: + - None if no fields need to be written + - Tuple + table_name : name of the intermediate table + field1 + field2 + """ + # variable declaration + own : TableFieldType + field_type : str + state: FieldSqlErrorType + primary : bool + error : str + is_view_field : bool + foreign : TableFieldType + foreign_type : str + table_name : str = "" + field1 : str = "" + field2 : str = "" + write_fields: tuple[str,str,str] | None = None + + # create TableFieldType own out of collection_name, field_name, value as field_def + own = TableFieldType(collection_name, field_name, value) + field_type = own.field_def.get("type", None) + + # get the foreign field list and check the relations + foreign_fields = InternalHelper.get_definitions_from_foreign_list(value.get("to", None), value.get("reference", None)) + state, primary, _, error = InternalHelper.check_relation_definitions(own, foreign_fields) + is_view_field = state == FieldSqlErrorType.SQL + + if primary: + if field_type == "relation-list": + foreign = foreign_fields[0] + foreign_type = foreign.field_def.get("type", None) + + if foreign_type == "relation-list": + table_name = HelperGetNames.get_nm_table_name(own, foreign) + field1 = HelperGetNames.get_field_in_n_m_relation_list( + own, foreign.table + ) + field2 = HelperGetNames.get_field_in_n_m_relation_list( + foreign, own.table + ) + if field1 == field2: + field1 += "_1" + field2 += "_2" + write_fields = (table_name, field1, field2) + + + elif field_type == "generic-relation-list": + table_name = HelperGetNames.get_gm_table_name(own) + field1 = f"{own.table}_{own.ref_column}" + field2 = own.column[:-1] + + write_fields = (table_name, field1, field2) - return field == FieldSqlErrorType.SQL + assert error == "", error + + return is_view_field, write_fields From e563c4654e2795152802e2caf6692950ef3ddaf6 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 12 Jun 2024 07:27:25 +0200 Subject: [PATCH 062/142] Moved functions to helper_get_names --- dev/src/generate_sql_schema.py | 6 +-- dev/src/helper_get_names.py | 90 +++++----------------------------- 2 files changed, 13 insertions(+), 83 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 3300fe85..61a8a9cb 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -11,11 +11,10 @@ from .helper_get_names import ( KEYSEPARATOR, + FieldSqlErrorType, HelperGetNames, InternalHelper, - GenerateHelper, TableFieldType, - FieldSqlErrorType, ) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() @@ -933,8 +932,6 @@ def get_initials( def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: return f"comment on column {entity_name}.{fname} is '{comment}';\n" - - @staticmethod def get_generic_combined_fields( generic_plain_field_name: str, own_column: str, foreign_table: str @@ -988,6 +985,7 @@ def get_foreign_table_from_to_or_reference( else: raise Exception("Relation field without reference or to") + FIELD_TYPES: dict[str, dict[str, Any]] = { "string": { "pg_type": string.Template("varchar(${maxLength})"), diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index e1c634c1..e13735bd 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -2,8 +2,8 @@ import os import re from collections.abc import Callable -from typing import Any, cast, TypedDict from enum import Enum +from typing import Any, TypedDict, cast import requests import yaml @@ -47,12 +47,14 @@ def get_definitions_from_foreign( return TableFieldType(tname, fname, tfield, ref_column) + class ToDict(TypedDict): """Defines the dict keys for the to-Attribute of generic relations in field definitions""" collections: list[str] field: str + class FieldSqlErrorType(Enum): FIELD = 1 SQL = 2 @@ -431,12 +433,16 @@ def check_relation_definitions( else: for i, foreign_field in enumerate(foreign_fields): if i == 0: - state, primary, error = InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] + state, primary, error = ( + InternalHelper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) ) else: - statex, primaryx, error = InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] + statex, primaryx, error = ( + InternalHelper.generate_field_or_sql_decision( + own_field, own_c, foreign_field, foreigns_c[i] + ) ) if not error and (statex != state or primaryx != primary): error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" @@ -482,77 +488,3 @@ def get_definitions_from_foreign_list( else: results.append(TableFieldType.get_definitions_from_foreign(to, None)) return results - - @staticmethod - def get_view_field_state_write_fields( - collection_name: str, field_name: str, value: dict[str, any] - ) -> (bool, tuple[str, str, str]): - """ - Purpose: - Checks whether a field is a view field and if other fields need to be written in an intermediate - table. - Input: - - collection_name - - field_name - - value : represents the definition of the field ( field_name in collection_name ) - Returns: - - is_view_field : whether the field is a view field or not - - write_fields: - - None if no fields need to be written - - Tuple - table_name : name of the intermediate table - field1 - field2 - """ - # variable declaration - own : TableFieldType - field_type : str - state: FieldSqlErrorType - primary : bool - error : str - is_view_field : bool - foreign : TableFieldType - foreign_type : str - table_name : str = "" - field1 : str = "" - field2 : str = "" - write_fields: tuple[str,str,str] | None = None - - # create TableFieldType own out of collection_name, field_name, value as field_def - own = TableFieldType(collection_name, field_name, value) - field_type = own.field_def.get("type", None) - - # get the foreign field list and check the relations - foreign_fields = InternalHelper.get_definitions_from_foreign_list(value.get("to", None), value.get("reference", None)) - state, primary, _, error = InternalHelper.check_relation_definitions(own, foreign_fields) - is_view_field = state == FieldSqlErrorType.SQL - - if primary: - if field_type == "relation-list": - foreign = foreign_fields[0] - foreign_type = foreign.field_def.get("type", None) - - if foreign_type == "relation-list": - table_name = HelperGetNames.get_nm_table_name(own, foreign) - field1 = HelperGetNames.get_field_in_n_m_relation_list( - own, foreign.table - ) - field2 = HelperGetNames.get_field_in_n_m_relation_list( - foreign, own.table - ) - if field1 == field2: - field1 += "_1" - field2 += "_2" - write_fields = (table_name, field1, field2) - - - elif field_type == "generic-relation-list": - table_name = HelperGetNames.get_gm_table_name(own) - field1 = f"{own.table}_{own.ref_column}" - field2 = own.column[:-1] - - write_fields = (table_name, field1, field2) - - assert error == "", error - - return is_view_field, write_fields From 312dad94e7e5d8fa00f5b330021ad02c36a68dec Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 12 Jul 2024 10:35:49 +0200 Subject: [PATCH 063/142] make CREATE EXTENSION idempotent --- dev/sql/schema_relational.sql | 2 +- dev/src/generate_sql_schema.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 18f370e6..2c194972 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. -CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine +CREATE EXTENSION IF NOT EXISTS hstore; -- included in standard postgres-installations, check for alpine create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 61a8a9cb..d99b54ac 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -575,7 +575,7 @@ class Helper: """ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. - CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine + CREATE EXTENSION hstore IF NOT EXISTS; -- included in standard postgres-installations, check for alpine create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: From e61d60595d3b5667b23e8bcf1ceb3793a43a3aa2 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 14 Aug 2024 16:23:37 +0200 Subject: [PATCH 064/142] Add reference for foreign key to field meeting/anonymous_group_id --- models.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/models.yml b/models.yml index b18193ef..ee565061 100644 --- a/models.yml +++ b/models.yml @@ -1949,6 +1949,7 @@ meeting: anonymous_group_id: type: relation to: group/anonymous_group_for_meeting_id + reference: group restriction_mode: B structure_level: From 717cb1c780d6f8cd650983675c0b3b7468d603ce Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 15 Aug 2024 15:58:26 +0200 Subject: [PATCH 065/142] Fix import and generate new relational schema --- dev/sql/schema_relational.sql | 300 +++++++++++++++++---------------- dev/src/generate_sql_schema.py | 4 +- 2 files changed, 158 insertions(+), 146 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 2c194972..f3795bd3 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. -CREATE EXTENSION IF NOT EXISTS hstore; -- included in standard postgres-installations, check for alpine +CREATE EXTENSION hstore IF NOT EXISTS; -- included in standard postgres-installations, check for alpine create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: @@ -55,7 +55,7 @@ BEGIN END; $$ LANGUAGE plpgsql; --- MODELS_YML_CHECKSUM = '0a7ce920e0e10eedd3b09c4ce81fc3f8' +-- MODELS_YML_CHECKSUM = 'e3bcb2c45850469b5e56d3542d12ea15' -- Type definitions -- Table definitions @@ -73,6 +73,7 @@ CREATE TABLE IF NOT EXISTS organization_t ( limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, default_language varchar(256) NOT NULL CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), + require_duplicate_from boolean, saml_enabled boolean, saml_login_button_text varchar(256) DEFAULT 'SAML login', saml_attr_mapping jsonb, @@ -111,6 +112,7 @@ comment on column organization_t.limit_of_users is 'Maximum of active users for CREATE TABLE IF NOT EXISTS user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, username varchar(256) NOT NULL, + member_number varchar(256), saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), pronoun varchar(32), title varchar(256), @@ -145,6 +147,7 @@ CREATE TABLE IF NOT EXISTS meeting_user_t ( number varchar(256), about_me text, vote_weight decimal(6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), + locked_out boolean, user_id integer NOT NULL, meeting_id integer NOT NULL, vote_delegated_to_id integer @@ -245,6 +248,7 @@ CREATE TABLE IF NOT EXISTS meeting_t ( location varchar(256), start_time timestamptz, end_time timestamptz, + locked_from_inside boolean, imported_at timestamptz, language varchar(256) NOT NULL CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), jitsi_domain varchar(256), @@ -373,6 +377,7 @@ This email was generated automatically.', users_forbid_delegator_in_list_of_speakers boolean, users_forbid_delegator_as_submitter boolean, users_forbid_delegator_as_supporter boolean, + users_forbid_delegator_to_vote boolean, assignments_export_title varchar(256) DEFAULT 'Elections', assignments_export_preamble text, assignment_poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_assignment_poll_ballot_paper_selection CHECK (assignment_poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')) DEFAULT 'CUSTOM_NUMBER', @@ -414,7 +419,8 @@ This email was generated automatically.', list_of_speakers_countdown_id integer, poll_countdown_id integer, default_group_id integer NOT NULL, - admin_group_id integer + admin_group_id integer, + anonymous_group_id integer ); @@ -957,7 +963,9 @@ CREATE TABLE IF NOT EXISTS projector_t ( header_font_color varchar(7) CHECK (header_font_color is null or header_font_color ~* '^#[a-f0-9]{6}$') DEFAULT '#f5f5f5', header_h1_color varchar(7) CHECK (header_h1_color is null or header_h1_color ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', chyron_background_color varchar(7) CHECK (chyron_background_color is null or chyron_background_color ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', + chyron_background_color_2 varchar(7) CHECK (chyron_background_color_2 is null or chyron_background_color_2 ~* '^#[a-f0-9]{6}$') DEFAULT '#134768', chyron_font_color varchar(7) CHECK (chyron_font_color is null or chyron_font_color ~* '^#[a-f0-9]{6}$') DEFAULT '#ffffff', + chyron_font_color_2 varchar(7) CHECK (chyron_font_color_2 is null or chyron_font_color_2 ~* '^#[a-f0-9]{6}$') DEFAULT '#ffffff', show_header_footer boolean DEFAULT True, show_title boolean DEFAULT True, show_logo boolean DEFAULT True, @@ -1249,14 +1257,14 @@ CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_group_t ( -- View definitions CREATE OR REPLACE VIEW organization AS SELECT *, -(select array_agg(c.id) from committee_t c) as committee_ids, +(select array_agg(c.id) from committee_t c where c.organization_id = o.id) as committee_ids, (select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, (select array_agg(m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(ot.id) from organization_tag_t ot) as organization_tag_ids, -(select array_agg(t.id) from theme_t t) as theme_ids, +(select array_agg(ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, +(select array_agg(t.id) from theme_t t where t.organization_id = o.id) as theme_ids, (select array_agg(m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, -(select array_agg(u.id) from user_t u) as user_ids +(select array_agg(u.id) from user_t u where u.organization_id = o.id) as user_ids FROM organization_t o; @@ -1385,6 +1393,7 @@ CREATE OR REPLACE VIEW group_ AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, +(select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, (select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_access_group_ids, (select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_inherited_access_group_ids, (select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, @@ -1628,6 +1637,7 @@ ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES ALTER TABLE meeting_t ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdown_t(id); ALTER TABLE meeting_t ADD FOREIGN KEY(default_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(admin_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(anonymous_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); @@ -1926,15 +1936,15 @@ Model.Field -> Model.Field */ /* -SQL nr: => organization/committee_ids:-> committee/ +SQL nr:1rR => organization/committee_ids:-> committee/organization_id SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id -SQL nr: => organization/organization_tag_ids:-> organization_tag/ -FIELD 1rR: => organization/theme_id:-> theme/ -SQL nr: => organization/theme_ids:-> theme/ +SQL nr:1rR => organization/organization_tag_ids:-> organization_tag/organization_id +FIELD 1rR:1t => organization/theme_id:-> theme/theme_for_organization_id +SQL nr:1rR => organization/theme_ids:-> theme/organization_id SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id -SQL nr: => organization/user_ids:-> user/ +SQL nr:1rR => organization/user_ids:-> user/organization_id SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nt:nt => user/committee_ids:-> committee/user_ids @@ -1947,8 +1957,8 @@ SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id -FIELD 1rR: => meeting_user/user_id:-> user/ -FIELD 1rR: => meeting_user/meeting_id:-> meeting/ +FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids +FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids @@ -1956,7 +1966,7 @@ SQL nt:1rR => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id SQL nt:1rR => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id SQL nt:1rR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id -FIELD 1r: => meeting_user/vote_delegated_to_id:-> meeting_user/ +FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id SQL nt:1rR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids @@ -1967,20 +1977,20 @@ SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,m SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id -FIELD 1r: => committee/default_meeting_id:-> meeting/ +FIELD 1r:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id SQL nt:nt => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids -FIELD 1r: => committee/forwarding_user_id:-> user/ +FIELD 1r:nt => committee/forwarding_user_id:-> user/forwarding_committee_ids SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids -FIELD 1r: => meeting/is_active_in_organization_id:-> organization/ -FIELD 1r: => meeting/is_archived_in_organization_id:-> organization/ -FIELD 1r: => meeting/template_for_organization_id:-> organization/ -FIELD 1rR: => meeting/motions_default_workflow_id:-> motion_workflow/ -FIELD 1rR: => meeting/motions_default_amendment_workflow_id:-> motion_workflow/ -FIELD 1rR: => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/ +FIELD 1r:nt => meeting/is_active_in_organization_id:-> organization/active_meeting_ids +FIELD 1r:nt => meeting/is_archived_in_organization_id:-> organization/archived_meeting_ids +FIELD 1r:nt => meeting/template_for_organization_id:-> organization/template_meeting_ids +FIELD 1rR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id +FIELD 1rR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id +FIELD 1rR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id SQL nt:1r => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id SQL nt:1rR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id SQL nt:1rR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id @@ -2023,29 +2033,29 @@ SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1rR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1rR => meeting/chat_message_ids:-> chat_message/meeting_id SQL nt:1rR => meeting/structure_level_ids:-> structure_level/meeting_id -FIELD 1r: => meeting/logo_projector_main_id:-> mediafile/ -FIELD 1r: => meeting/logo_projector_header_id:-> mediafile/ -FIELD 1r: => meeting/logo_web_header_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_header_l_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_header_r_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_footer_l_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_footer_r_id:-> mediafile/ -FIELD 1r: => meeting/logo_pdf_ballot_paper_id:-> mediafile/ -FIELD 1r: => meeting/font_regular_id:-> mediafile/ -FIELD 1r: => meeting/font_italic_id:-> mediafile/ -FIELD 1r: => meeting/font_bold_id:-> mediafile/ -FIELD 1r: => meeting/font_bold_italic_id:-> mediafile/ -FIELD 1r: => meeting/font_monospace_id:-> mediafile/ -FIELD 1r: => meeting/font_chyron_speaker_name_id:-> mediafile/ -FIELD 1r: => meeting/font_projector_h1_id:-> mediafile/ -FIELD 1r: => meeting/font_projector_h2_id:-> mediafile/ -FIELD 1rR: => meeting/committee_id:-> committee/ +FIELD 1r:1t => meeting/logo_projector_main_id:-> mediafile/used_as_logo_projector_main_in_meeting_id +FIELD 1r:1t => meeting/logo_projector_header_id:-> mediafile/used_as_logo_projector_header_in_meeting_id +FIELD 1r:1t => meeting/logo_web_header_id:-> mediafile/used_as_logo_web_header_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_header_l_id:-> mediafile/used_as_logo_pdf_header_l_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_header_r_id:-> mediafile/used_as_logo_pdf_header_r_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_footer_l_id:-> mediafile/used_as_logo_pdf_footer_l_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_footer_r_id:-> mediafile/used_as_logo_pdf_footer_r_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_ballot_paper_id:-> mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id +FIELD 1r:1t => meeting/font_regular_id:-> mediafile/used_as_font_regular_in_meeting_id +FIELD 1r:1t => meeting/font_italic_id:-> mediafile/used_as_font_italic_in_meeting_id +FIELD 1r:1t => meeting/font_bold_id:-> mediafile/used_as_font_bold_in_meeting_id +FIELD 1r:1t => meeting/font_bold_italic_id:-> mediafile/used_as_font_bold_italic_in_meeting_id +FIELD 1r:1t => meeting/font_monospace_id:-> mediafile/used_as_font_monospace_in_meeting_id +FIELD 1r:1t => meeting/font_chyron_speaker_name_id:-> mediafile/used_as_font_chyron_speaker_name_in_meeting_id +FIELD 1r:1t => meeting/font_projector_h1_id:-> mediafile/used_as_font_projector_h1_in_meeting_id +FIELD 1r:1t => meeting/font_projector_h2_id:-> mediafile/used_as_font_projector_h2_in_meeting_id +FIELD 1rR:nt => meeting/committee_id:-> committee/meeting_ids SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids -FIELD 1rR: => meeting/reference_projector_id:-> projector/ -FIELD 1r: => meeting/list_of_speakers_countdown_id:-> projector_countdown/ -FIELD 1r: => meeting/poll_countdown_id:-> projector_countdown/ +FIELD 1rR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id +FIELD 1r:1t => meeting/list_of_speakers_countdown_id:-> projector_countdown/used_as_list_of_speakers_countdown_meeting_id +FIELD 1r:1t => meeting/poll_countdown_id:-> projector_countdown/used_as_poll_countdown_meeting_id SQL nt:1GrR => meeting/projection_ids:-> projection/content_object_id SQL ntR:1r => meeting/default_projector_agenda_item_list_ids:-> projector/used_as_default_projector_for_agenda_item_list_in_meeting_id SQL ntR:1r => meeting/default_projector_topic_ids:-> projector/used_as_default_projector_for_topic_in_meeting_id @@ -2061,16 +2071,18 @@ SQL ntR:1r => meeting/default_projector_countdown_ids:-> projector/used_as_defau SQL ntR:1r => meeting/default_projector_assignment_poll_ids:-> projector/used_as_default_projector_for_assignment_poll_in_meeting_id SQL ntR:1r => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id -FIELD 1rR: => meeting/default_group_id:-> group/ -FIELD 1r: => meeting/admin_group_id:-> group/ +FIELD 1rR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id +FIELD 1r:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id +FIELD 1r:1t => meeting/anonymous_group_id:-> group/anonymous_group_for_meeting_id SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id -FIELD 1rR: => structure_level/meeting_id:-> meeting/ +FIELD 1rR:nt => structure_level/meeting_id:-> meeting/structure_level_ids SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id +SQL 1t:1r => group/anonymous_group_for_meeting_id:-> meeting/anonymous_group_id SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids @@ -2078,71 +2090,71 @@ SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_gro SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids SQL nt:nt => group/write_chat_group_ids:-> chat_group/write_group_ids SQL nt:nt => group/poll_ids:-> poll/entitled_group_ids -FIELD 1r: => group/used_as_motion_poll_default_id:-> meeting/ -FIELD 1r: => group/used_as_assignment_poll_default_id:-> meeting/ -FIELD 1r: => group/used_as_topic_poll_default_id:-> meeting/ -FIELD 1r: => group/used_as_poll_default_id:-> meeting/ -FIELD 1rR: => group/meeting_id:-> meeting/ +FIELD 1r:nt => group/used_as_motion_poll_default_id:-> meeting/motion_poll_default_group_ids +FIELD 1r:nt => group/used_as_assignment_poll_default_id:-> meeting/assignment_poll_default_group_ids +FIELD 1r:nt => group/used_as_topic_poll_default_id:-> meeting/topic_poll_default_group_ids +FIELD 1r:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids +FIELD 1rR:nt => group/meeting_id:-> meeting/group_ids -FIELD 1rR: => personal_note/meeting_user_id:-> meeting_user/ +FIELD 1rR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids FIELD 1Gr: => personal_note/content_object_id:-> motion/ -FIELD 1rR: => personal_note/meeting_id:-> meeting/ +FIELD 1rR:nt => personal_note/meeting_id:-> meeting/personal_note_ids SQL nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids -FIELD 1rR: => tag/meeting_id:-> meeting/ +FIELD 1rR:nt => tag/meeting_id:-> meeting/tag_ids FIELD 1GrR:,,, => agenda_item/content_object_id:-> motion/,motion_block/,assignment/,topic/ -FIELD 1r: => agenda_item/parent_id:-> agenda_item/ +FIELD 1r:nt => agenda_item/parent_id:-> agenda_item/child_ids SQL nt:1r => agenda_item/child_ids:-> agenda_item/parent_id SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id -FIELD 1rR: => agenda_item/meeting_id:-> meeting/ +FIELD 1rR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids FIELD 1GrR:,,,, => list_of_speakers/content_object_id:-> motion/,motion_block/,assignment/,topic/,mediafile/ SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id -FIELD 1rR: => list_of_speakers/meeting_id:-> meeting/ +FIELD 1rR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids -FIELD 1rR: => structure_level_list_of_speakers/structure_level_id:-> structure_level/ -FIELD 1rR: => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/ +FIELD 1rR:nt => structure_level_list_of_speakers/structure_level_id:-> structure_level/structure_level_list_of_speakers_ids +FIELD 1rR:nt => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/structure_level_list_of_speakers_ids SQL nt:1r => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id -FIELD 1rR: => structure_level_list_of_speakers/meeting_id:-> meeting/ +FIELD 1rR:nt => structure_level_list_of_speakers/meeting_id:-> meeting/structure_level_list_of_speakers_ids -FIELD 1rR: => point_of_order_category/meeting_id:-> meeting/ +FIELD 1rR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids SQL nt:1r => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id -FIELD 1rR: => speaker/list_of_speakers_id:-> list_of_speakers/ -FIELD 1r: => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/ -FIELD 1r: => speaker/meeting_user_id:-> meeting_user/ -FIELD 1r: => speaker/point_of_order_category_id:-> point_of_order_category/ -FIELD 1rR: => speaker/meeting_id:-> meeting/ +FIELD 1rR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids +FIELD 1r:nt => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/speaker_ids +FIELD 1r:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids +FIELD 1r:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids +FIELD 1rR:nt => speaker/meeting_id:-> meeting/speaker_ids SQL nt:nGt => topic/attachment_ids:-> mediafile/attachment_ids SQL 1tR:1GrR => topic/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:1GrR => topic/poll_ids:-> poll/content_object_id SQL nt:1GrR => topic/projection_ids:-> projection/content_object_id -FIELD 1rR: => topic/meeting_id:-> meeting/ +FIELD 1rR:nt => topic/meeting_id:-> meeting/topic_ids -FIELD 1r: => motion/lead_motion_id:-> motion/ +FIELD 1r:nt => motion/lead_motion_id:-> motion/amendment_ids SQL nt:1r => motion/amendment_ids:-> motion/lead_motion_id -FIELD 1r: => motion/sort_parent_id:-> motion/ +FIELD 1r:nt => motion/sort_parent_id:-> motion/sort_child_ids SQL nt:1r => motion/sort_child_ids:-> motion/sort_parent_id -FIELD 1r: => motion/origin_id:-> motion/ -FIELD 1r: => motion/origin_meeting_id:-> meeting/ +FIELD 1r:nt => motion/origin_id:-> motion/derived_motion_ids +FIELD 1r:nt => motion/origin_meeting_id:-> meeting/forwarded_motion_ids SQL nt:1r => motion/derived_motion_ids:-> motion/origin_id SQL nt:nt => motion/all_origin_ids:-> motion/all_derived_motion_ids SQL nt:nt => motion/all_derived_motion_ids:-> motion/all_origin_ids SQL nt:nt => motion/identical_motion_ids:-> motion/identical_motion_ids -FIELD 1rR: => motion/state_id:-> motion_state/ -FIELD 1r: => motion/recommendation_id:-> motion_state/ +FIELD 1rR:nt => motion/state_id:-> motion_state/motion_ids +FIELD 1r:nt => motion/recommendation_id:-> motion_state/motion_recommendation_ids SQL nGt:nt => motion/state_extension_reference_ids:-> motion/referenced_in_motion_state_extension_ids SQL nt:nGt => motion/referenced_in_motion_state_extension_ids:-> motion/state_extension_reference_ids SQL nGt:nt => motion/recommendation_extension_reference_ids:-> motion/referenced_in_motion_recommendation_extension_ids SQL nt:nGt => motion/referenced_in_motion_recommendation_extension_ids:-> motion/recommendation_extension_reference_ids -FIELD 1r: => motion/category_id:-> motion_category/ -FIELD 1r: => motion/block_id:-> motion_block/ +FIELD 1r:nt => motion/category_id:-> motion_category/motion_ids +FIELD 1r:nt => motion/block_id:-> motion_block/motion_ids SQL nt:1rR => motion/submitter_ids:-> motion_submitter/motion_id SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ids SQL nt:1rR => motion/editor_ids:-> motion_editor/motion_id @@ -2150,7 +2162,7 @@ SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/m SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id SQL nt:1Gr => motion/option_ids:-> option/content_object_id SQL nt:1rR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id -FIELD 1r: => motion/statute_paragraph_id:-> motion_statute_paragraph/ +FIELD 1r:nt => motion/statute_paragraph_id:-> motion_statute_paragraph/motion_ids SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id @@ -2158,81 +2170,81 @@ SQL nt:nGt => motion/tag_ids:-> tag/tagged_ids SQL nt:nGt => motion/attachment_ids:-> mediafile/attachment_ids SQL nt:1GrR => motion/projection_ids:-> projection/content_object_id SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id -FIELD 1rR: => motion/meeting_id:-> meeting/ +FIELD 1rR:nt => motion/meeting_id:-> meeting/motion_ids -FIELD 1rR: => motion_submitter/meeting_user_id:-> meeting_user/ -FIELD 1rR: => motion_submitter/motion_id:-> motion/ -FIELD 1rR: => motion_submitter/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids +FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids +FIELD 1rR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids -FIELD 1rR: => motion_editor/meeting_user_id:-> meeting_user/ -FIELD 1rR: => motion_editor/motion_id:-> motion/ -FIELD 1rR: => motion_editor/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids +FIELD 1rR:nt => motion_editor/motion_id:-> motion/editor_ids +FIELD 1rR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids -FIELD 1rR: => motion_working_group_speaker/meeting_user_id:-> meeting_user/ -FIELD 1rR: => motion_working_group_speaker/motion_id:-> motion/ -FIELD 1rR: => motion_working_group_speaker/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids +FIELD 1rR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids +FIELD 1rR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids -FIELD 1rR: => motion_comment/motion_id:-> motion/ -FIELD 1rR: => motion_comment/section_id:-> motion_comment_section/ -FIELD 1rR: => motion_comment/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_comment/motion_id:-> motion/comment_ids +FIELD 1rR:nt => motion_comment/section_id:-> motion_comment_section/comment_ids +FIELD 1rR:nt => motion_comment/meeting_id:-> meeting/motion_comment_ids SQL nt:1rR => motion_comment_section/comment_ids:-> motion_comment/section_id SQL nt:nt => motion_comment_section/read_group_ids:-> group/read_comment_section_ids SQL nt:nt => motion_comment_section/write_group_ids:-> group/write_comment_section_ids -FIELD 1rR: => motion_comment_section/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_comment_section/meeting_id:-> meeting/motion_comment_section_ids -FIELD 1r: => motion_category/parent_id:-> motion_category/ +FIELD 1r:nt => motion_category/parent_id:-> motion_category/child_ids SQL nt:1r => motion_category/child_ids:-> motion_category/parent_id SQL nt:1r => motion_category/motion_ids:-> motion/category_id -FIELD 1rR: => motion_category/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_category/meeting_id:-> meeting/motion_category_ids SQL nt:1r => motion_block/motion_ids:-> motion/block_id SQL 1t:1GrR => motion_block/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:1GrR => motion_block/projection_ids:-> projection/content_object_id -FIELD 1rR: => motion_block/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_block/meeting_id:-> meeting/motion_block_ids -FIELD 1rR: => motion_change_recommendation/motion_id:-> motion/ -FIELD 1rR: => motion_change_recommendation/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_change_recommendation/motion_id:-> motion/change_recommendation_ids +FIELD 1rR:nt => motion_change_recommendation/meeting_id:-> meeting/motion_change_recommendation_ids -FIELD 1r: => motion_state/submitter_withdraw_state_id:-> motion_state/ +FIELD 1r:nt => motion_state/submitter_withdraw_state_id:-> motion_state/submitter_withdraw_back_ids SQL nt:1r => motion_state/submitter_withdraw_back_ids:-> motion_state/submitter_withdraw_state_id SQL nt:nt => motion_state/next_state_ids:-> motion_state/previous_state_ids SQL nt:nt => motion_state/previous_state_ids:-> motion_state/next_state_ids SQL nt:1rR => motion_state/motion_ids:-> motion/state_id SQL nt:1r => motion_state/motion_recommendation_ids:-> motion/recommendation_id -FIELD 1rR: => motion_state/workflow_id:-> motion_workflow/ +FIELD 1rR:nt => motion_state/workflow_id:-> motion_workflow/state_ids SQL 1t:1rR => motion_state/first_state_of_workflow_id:-> motion_workflow/first_state_id -FIELD 1rR: => motion_state/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_state/meeting_id:-> meeting/motion_state_ids SQL nt:1rR => motion_workflow/state_ids:-> motion_state/workflow_id -FIELD 1rR: => motion_workflow/first_state_id:-> motion_state/ +FIELD 1rR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id SQL 1t:1rR => motion_workflow/default_statute_amendment_workflow_meeting_id:-> meeting/motions_default_statute_amendment_workflow_id -FIELD 1rR: => motion_workflow/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids SQL nt:1r => motion_statute_paragraph/motion_ids:-> motion/statute_paragraph_id -FIELD 1rR: => motion_statute_paragraph/meeting_id:-> meeting/ +FIELD 1rR:nt => motion_statute_paragraph/meeting_id:-> meeting/motion_statute_paragraph_ids FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ SQL nt:1r => poll/option_ids:-> option/poll_id -FIELD 1r: => poll/global_option_id:-> option/ +FIELD 1r:1t => poll/global_option_id:-> option/used_as_global_option_in_poll_id SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id -FIELD 1rR: => poll/meeting_id:-> meeting/ +FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids -FIELD 1r: => option/poll_id:-> poll/ +FIELD 1r:nt => option/poll_id:-> poll/option_ids SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id SQL nt:1rR => option/vote_ids:-> vote/option_id FIELD 1Gr:,, => option/content_object_id:-> motion/,user/,poll_candidate_list/ -FIELD 1rR: => option/meeting_id:-> meeting/ +FIELD 1rR:nt => option/meeting_id:-> meeting/option_ids -FIELD 1rR: => vote/option_id:-> option/ -FIELD 1r: => vote/user_id:-> user/ -FIELD 1r: => vote/delegated_user_id:-> user/ -FIELD 1rR: => vote/meeting_id:-> meeting/ +FIELD 1rR:nt => vote/option_id:-> option/vote_ids +FIELD 1r:nt => vote/user_id:-> user/vote_ids +FIELD 1r:nt => vote/delegated_user_id:-> user/delegated_vote_ids +FIELD 1rR:nt => vote/meeting_id:-> meeting/vote_ids SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id @@ -2241,23 +2253,23 @@ SQL 1tR:1GrR => assignment/list_of_speakers_id:-> list_of_speakers/content_objec SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids SQL nt:nGt => assignment/attachment_ids:-> mediafile/attachment_ids SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id -FIELD 1rR: => assignment/meeting_id:-> meeting/ +FIELD 1rR:nt => assignment/meeting_id:-> meeting/assignment_ids -FIELD 1rR: => assignment_candidate/assignment_id:-> assignment/ -FIELD 1r: => assignment_candidate/meeting_user_id:-> meeting_user/ -FIELD 1rR: => assignment_candidate/meeting_id:-> meeting/ +FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids +FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids +FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids SQL nt:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id -FIELD 1rR: => poll_candidate_list/meeting_id:-> meeting/ +FIELD 1rR:nt => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id -FIELD 1rR: => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/ -FIELD 1r: => poll_candidate/user_id:-> user/ -FIELD 1rR: => poll_candidate/meeting_id:-> meeting/ +FIELD 1rR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids +FIELD 1r:nt => poll_candidate/user_id:-> user/poll_candidate_ids +FIELD 1rR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids SQL nt:nt => mediafile/inherited_access_group_ids:-> group/mediafile_inherited_access_group_ids SQL nt:nt => mediafile/access_group_ids:-> group/mediafile_access_group_ids -FIELD 1r: => mediafile/parent_id:-> mediafile/ +FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id SQL 1t:1GrR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:1GrR => mediafile/projection_ids:-> projection/content_object_id @@ -2284,44 +2296,44 @@ SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id SQL nt:1r => projector/history_projection_ids:-> projection/history_projector_id SQL 1t:1rR => projector/used_as_reference_projector_meeting_id:-> meeting/reference_projector_id -FIELD 1r: => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/ -FIELD 1r: => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/ -FIELD 1rR: => projector/meeting_id:-> meeting/ - -FIELD 1r: => projection/current_projector_id:-> projector/ -FIELD 1r: => projection/preview_projector_id:-> projector/ -FIELD 1r: => projection/history_projector_id:-> projector/ +FIELD 1r:ntR => projector/used_as_default_projector_for_agenda_item_list_in_meeting_id:-> meeting/default_projector_agenda_item_list_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_topic_in_meeting_id:-> meeting/default_projector_topic_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_list_of_speakers_in_meeting_id:-> meeting/default_projector_list_of_speakers_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_current_los_in_meeting_id:-> meeting/default_projector_current_los_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_motion_in_meeting_id:-> meeting/default_projector_motion_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_amendment_in_meeting_id:-> meeting/default_projector_amendment_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_motion_block_in_meeting_id:-> meeting/default_projector_motion_block_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_assignment_in_meeting_id:-> meeting/default_projector_assignment_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_mediafile_in_meeting_id:-> meeting/default_projector_mediafile_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_message_in_meeting_id:-> meeting/default_projector_message_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_countdown_in_meeting_id:-> meeting/default_projector_countdown_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_assignment_poll_in_meeting_id:-> meeting/default_projector_assignment_poll_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_id:-> meeting/default_projector_motion_poll_ids +FIELD 1r:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids +FIELD 1rR:nt => projector/meeting_id:-> meeting/projector_ids + +FIELD 1r:nt => projection/current_projector_id:-> projector/current_projection_ids +FIELD 1r:nt => projection/preview_projector_id:-> projector/preview_projection_ids +FIELD 1r:nt => projection/history_projector_id:-> projector/history_projection_ids FIELD 1GrR:,,,,,,,,,, => projection/content_object_id:-> meeting/,motion/,mediafile/,list_of_speakers/,motion_block/,assignment/,agenda_item/,topic/,poll/,projector_message/,projector_countdown/ -FIELD 1rR: => projection/meeting_id:-> meeting/ +FIELD 1rR:nt => projection/meeting_id:-> meeting/all_projection_ids SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id -FIELD 1rR: => projector_message/meeting_id:-> meeting/ +FIELD 1rR:nt => projector_message/meeting_id:-> meeting/projector_message_ids SQL nt:1GrR => projector_countdown/projection_ids:-> projection/content_object_id SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id -FIELD 1rR: => projector_countdown/meeting_id:-> meeting/ +FIELD 1rR:nt => projector_countdown/meeting_id:-> meeting/projector_countdown_ids SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids -FIELD 1rR: => chat_group/meeting_id:-> meeting/ +FIELD 1rR:nt => chat_group/meeting_id:-> meeting/chat_group_ids -FIELD 1rR: => chat_message/meeting_user_id:-> meeting_user/ -FIELD 1rR: => chat_message/chat_group_id:-> chat_group/ -FIELD 1rR: => chat_message/meeting_id:-> meeting/ +FIELD 1rR:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids +FIELD 1rR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids +FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids */ /* diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index d99b54ac..09c662bb 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -9,9 +9,9 @@ from textwrap import dedent from typing import Any, TypedDict, cast -from .helper_get_names import ( +from helper_get_names import FieldSqlErrorType # type: ignore +from helper_get_names import ( KEYSEPARATOR, - FieldSqlErrorType, HelperGetNames, InternalHelper, TableFieldType, From 24f09dc9ebe40bbb3a2fd1e2fff01f7c6a334864 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 15 Aug 2024 17:31:01 +0200 Subject: [PATCH 066/142] remove idempotency from relational schema (should always be done by migration) --- dev/sql/schema_relational.sql | 204 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 22 ++-- 2 files changed, 111 insertions(+), 115 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index f3795bd3..8c35948d 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,9 +1,9 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. -CREATE EXTENSION hstore IF NOT EXISTS; -- included in standard postgres-installations, check for alpine +CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine -create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ +CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name of field to check, usually a field in a view -- column_name of field to check @@ -43,7 +43,7 @@ begin end; $not_null_trigger$ language plpgsql; -CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ +CREATE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ DECLARE statements CURSOR FOR SELECT tablename FROM pg_tables @@ -59,7 +59,7 @@ $$ LANGUAGE plpgsql; -- Type definitions -- Table definitions -CREATE TABLE IF NOT EXISTS organization_t ( +CREATE TABLE organization_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256), description text, @@ -109,7 +109,7 @@ comment on column organization_t.limit_of_users is 'Maximum of active users for */ -CREATE TABLE IF NOT EXISTS user_t ( +CREATE TABLE user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, username varchar(256) NOT NULL, member_number varchar(256), @@ -141,7 +141,7 @@ comment on column user_t.organization_management_level is 'Hierarchical permissi comment on column user_t.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; -CREATE TABLE IF NOT EXISTS meeting_user_t ( +CREATE TABLE meeting_user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, comment text, number varchar(256), @@ -156,7 +156,7 @@ CREATE TABLE IF NOT EXISTS meeting_user_t ( -CREATE TABLE IF NOT EXISTS organization_tag_t ( +CREATE TABLE organization_tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') NOT NULL, @@ -166,7 +166,7 @@ CREATE TABLE IF NOT EXISTS organization_tag_t ( -CREATE TABLE IF NOT EXISTS theme_t ( +CREATE TABLE theme_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, accent_100 varchar(7) CHECK (accent_100 is null or accent_100 ~* '^#[a-f0-9]{6}$'), @@ -221,7 +221,7 @@ CREATE TABLE IF NOT EXISTS theme_t ( -CREATE TABLE IF NOT EXISTS committee_t ( +CREATE TABLE committee_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, description text, @@ -236,7 +236,7 @@ CREATE TABLE IF NOT EXISTS committee_t ( comment on column committee_t.external_id is 'unique'; -CREATE TABLE IF NOT EXISTS meeting_t ( +CREATE TABLE meeting_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', @@ -433,7 +433,7 @@ comment on column meeting_t.list_of_speakers_intervention_time is '0 disables in comment on column meeting_t.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; -CREATE TABLE IF NOT EXISTS structure_level_t ( +CREATE TABLE structure_level_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$'), @@ -444,7 +444,7 @@ CREATE TABLE IF NOT EXISTS structure_level_t ( -CREATE TABLE IF NOT EXISTS group_t ( +CREATE TABLE group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), name varchar(256) NOT NULL, @@ -462,7 +462,7 @@ CREATE TABLE IF NOT EXISTS group_t ( comment on column group_t.external_id is 'unique in meeting'; -CREATE TABLE IF NOT EXISTS personal_note_t ( +CREATE TABLE personal_note_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, note text, star boolean, @@ -476,7 +476,7 @@ CREATE TABLE IF NOT EXISTS personal_note_t ( -CREATE TABLE IF NOT EXISTS tag_t ( +CREATE TABLE tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, meeting_id integer NOT NULL @@ -485,7 +485,7 @@ CREATE TABLE IF NOT EXISTS tag_t ( -CREATE TABLE IF NOT EXISTS agenda_item_t ( +CREATE TABLE agenda_item_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, item_number varchar(256), comment varchar(256), @@ -515,7 +515,7 @@ comment on column agenda_item_t.is_hidden is 'Calculated by the server'; comment on column agenda_item_t.level is 'Calculated by the server'; -CREATE TABLE IF NOT EXISTS list_of_speakers_t ( +CREATE TABLE list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, closed boolean DEFAULT False, sequential_number integer NOT NULL, @@ -534,7 +534,7 @@ CREATE TABLE IF NOT EXISTS list_of_speakers_t ( comment on column list_of_speakers_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS structure_level_list_of_speakers_t ( +CREATE TABLE structure_level_list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, structure_level_id integer NOT NULL, list_of_speakers_id integer NOT NULL, @@ -553,7 +553,7 @@ comment on column structure_level_list_of_speakers_t.remaining_time is 'The curr comment on column structure_level_list_of_speakers_t.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; -CREATE TABLE IF NOT EXISTS point_of_order_category_t ( +CREATE TABLE point_of_order_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, text varchar(256) NOT NULL, rank integer NOT NULL, @@ -563,7 +563,7 @@ CREATE TABLE IF NOT EXISTS point_of_order_category_t ( -CREATE TABLE IF NOT EXISTS speaker_t ( +CREATE TABLE speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, begin_time timestamptz, end_time timestamptz, @@ -584,7 +584,7 @@ CREATE TABLE IF NOT EXISTS speaker_t ( -CREATE TABLE IF NOT EXISTS topic_t ( +CREATE TABLE topic_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, text text, @@ -597,7 +597,7 @@ CREATE TABLE IF NOT EXISTS topic_t ( comment on column topic_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_t ( +CREATE TABLE motion_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, number varchar(256), number_value integer, @@ -636,7 +636,7 @@ comment on column motion_t.number_value is 'The number value of this motion. Thi comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_submitter_t ( +CREATE TABLE motion_submitter_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -647,7 +647,7 @@ CREATE TABLE IF NOT EXISTS motion_submitter_t ( -CREATE TABLE IF NOT EXISTS motion_editor_t ( +CREATE TABLE motion_editor_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -658,7 +658,7 @@ CREATE TABLE IF NOT EXISTS motion_editor_t ( -CREATE TABLE IF NOT EXISTS motion_working_group_speaker_t ( +CREATE TABLE motion_working_group_speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer, meeting_user_id integer NOT NULL, @@ -669,7 +669,7 @@ CREATE TABLE IF NOT EXISTS motion_working_group_speaker_t ( -CREATE TABLE IF NOT EXISTS motion_comment_t ( +CREATE TABLE motion_comment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, comment text, motion_id integer NOT NULL, @@ -680,7 +680,7 @@ CREATE TABLE IF NOT EXISTS motion_comment_t ( -CREATE TABLE IF NOT EXISTS motion_comment_section_t ( +CREATE TABLE motion_comment_section_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer DEFAULT 10000, @@ -694,7 +694,7 @@ CREATE TABLE IF NOT EXISTS motion_comment_section_t ( comment on column motion_comment_section_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_category_t ( +CREATE TABLE motion_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, prefix varchar(256), @@ -711,7 +711,7 @@ comment on column motion_category_t.level is 'Calculated field.'; comment on column motion_category_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_block_t ( +CREATE TABLE motion_block_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, internal boolean, @@ -724,7 +724,7 @@ CREATE TABLE IF NOT EXISTS motion_block_t ( comment on column motion_block_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_change_recommendation_t ( +CREATE TABLE motion_change_recommendation_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, rejected boolean DEFAULT False, internal boolean DEFAULT False, @@ -741,7 +741,7 @@ CREATE TABLE IF NOT EXISTS motion_change_recommendation_t ( -CREATE TABLE IF NOT EXISTS motion_state_t ( +CREATE TABLE motion_state_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer NOT NULL, @@ -766,7 +766,7 @@ CREATE TABLE IF NOT EXISTS motion_state_t ( -CREATE TABLE IF NOT EXISTS motion_workflow_t ( +CREATE TABLE motion_workflow_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, sequential_number integer NOT NULL, @@ -779,7 +779,7 @@ CREATE TABLE IF NOT EXISTS motion_workflow_t ( comment on column motion_workflow_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS motion_statute_paragraph_t ( +CREATE TABLE motion_statute_paragraph_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, text text, @@ -793,7 +793,7 @@ CREATE TABLE IF NOT EXISTS motion_statute_paragraph_t ( comment on column motion_statute_paragraph_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS poll_t ( +CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, description text, title varchar(256) NOT NULL, @@ -842,7 +842,7 @@ comment on column poll_t.votes_signature is 'base64 signature of votes_raw field */ -CREATE TABLE IF NOT EXISTS option_t ( +CREATE TABLE option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer DEFAULT 10000, text text, @@ -861,7 +861,7 @@ CREATE TABLE IF NOT EXISTS option_t ( -CREATE TABLE IF NOT EXISTS vote_t ( +CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight decimal(6), value varchar(256), @@ -875,7 +875,7 @@ CREATE TABLE IF NOT EXISTS vote_t ( -CREATE TABLE IF NOT EXISTS assignment_t ( +CREATE TABLE assignment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, description text, @@ -892,7 +892,7 @@ CREATE TABLE IF NOT EXISTS assignment_t ( comment on column assignment_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS assignment_candidate_t ( +CREATE TABLE assignment_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, weight integer DEFAULT 10000, assignment_id integer NOT NULL, @@ -903,7 +903,7 @@ CREATE TABLE IF NOT EXISTS assignment_candidate_t ( -CREATE TABLE IF NOT EXISTS poll_candidate_list_t ( +CREATE TABLE poll_candidate_list_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, meeting_id integer NOT NULL ); @@ -911,7 +911,7 @@ CREATE TABLE IF NOT EXISTS poll_candidate_list_t ( -CREATE TABLE IF NOT EXISTS poll_candidate_t ( +CREATE TABLE poll_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, poll_candidate_list_id integer NOT NULL, user_id integer, @@ -922,7 +922,7 @@ CREATE TABLE IF NOT EXISTS poll_candidate_t ( -CREATE TABLE IF NOT EXISTS mediafile_t ( +CREATE TABLE mediafile_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256), is_directory boolean, @@ -948,7 +948,7 @@ comment on column mediafile_t.filename is 'The uploaded filename. Will be used f comment on column mediafile_t.is_public is 'Calculated field. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; -CREATE TABLE IF NOT EXISTS projector_t ( +CREATE TABLE projector_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256), is_internal boolean DEFAULT False, @@ -993,7 +993,7 @@ CREATE TABLE IF NOT EXISTS projector_t ( comment on column projector_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE IF NOT EXISTS projection_t ( +CREATE TABLE projection_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, options jsonb, stable boolean DEFAULT False, @@ -1027,7 +1027,7 @@ CREATE TABLE IF NOT EXISTS projection_t ( */ -CREATE TABLE IF NOT EXISTS projector_message_t ( +CREATE TABLE projector_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, message text, meeting_id integer NOT NULL @@ -1036,7 +1036,7 @@ CREATE TABLE IF NOT EXISTS projector_message_t ( -CREATE TABLE IF NOT EXISTS projector_countdown_t ( +CREATE TABLE projector_countdown_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, title varchar(256) NOT NULL, description varchar(256) DEFAULT '', @@ -1049,7 +1049,7 @@ CREATE TABLE IF NOT EXISTS projector_countdown_t ( -CREATE TABLE IF NOT EXISTS chat_group_t ( +CREATE TABLE chat_group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, weight integer DEFAULT 10000, @@ -1059,7 +1059,7 @@ CREATE TABLE IF NOT EXISTS chat_group_t ( -CREATE TABLE IF NOT EXISTS chat_message_t ( +CREATE TABLE chat_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, content text NOT NULL, created timestamptz NOT NULL, @@ -1071,7 +1071,7 @@ CREATE TABLE IF NOT EXISTS chat_message_t ( -CREATE TABLE IF NOT EXISTS action_worker_t ( +CREATE TABLE action_worker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL, state varchar(256) NOT NULL CONSTRAINT enum_action_worker_state CHECK (state IN ('running', 'end', 'aborted')), @@ -1083,7 +1083,7 @@ CREATE TABLE IF NOT EXISTS action_worker_t ( -CREATE TABLE IF NOT EXISTS import_preview_t ( +CREATE TABLE import_preview_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256) NOT NULL CONSTRAINT enum_import_preview_name CHECK (name IN ('account', 'participant', 'topic', 'committee', 'motion')), state varchar(256) NOT NULL CONSTRAINT enum_import_preview_state CHECK (state IN ('warning', 'error', 'done')), @@ -1097,19 +1097,19 @@ CREATE TABLE IF NOT EXISTS import_preview_t ( -- Intermediate table definitions -CREATE TABLE IF NOT EXISTS nm_meeting_user_supported_motion_ids_motion_t ( +CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), motion_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (meeting_user_id, motion_id) ); -CREATE TABLE IF NOT EXISTS nm_meeting_user_structure_level_ids_structure_level_t ( +CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), structure_level_id integer NOT NULL REFERENCES structure_level_t (id), PRIMARY KEY (meeting_user_id, structure_level_id) ); -CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( +CREATE TABLE gm_organization_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id), tagged_id varchar(100) NOT NULL, @@ -1119,67 +1119,67 @@ CREATE TABLE IF NOT EXISTS gm_organization_tag_tagged_ids_t ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_user_ids_user_t ( +CREATE TABLE nm_committee_user_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_manager_ids_user_t ( +CREATE TABLE nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE IF NOT EXISTS nm_committee_forward_to_committee_ids_committee_t ( +CREATE TABLE nm_committee_forward_to_committee_ids_committee_t ( forward_to_committee_id integer NOT NULL REFERENCES committee_t (id), receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id), PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); -CREATE TABLE IF NOT EXISTS nm_meeting_present_user_ids_user_t ( +CREATE TABLE nm_meeting_present_user_ids_user_t ( meeting_id integer NOT NULL REFERENCES meeting_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (meeting_id, user_id) ); -CREATE TABLE IF NOT EXISTS nm_group_meeting_user_ids_meeting_user_t ( +CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( group_id integer NOT NULL REFERENCES group_t (id), meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), PRIMARY KEY (group_id, meeting_user_id) ); -CREATE TABLE IF NOT EXISTS nm_group_mediafile_access_group_ids_mediafile_t ( +CREATE TABLE nm_group_mediafile_access_group_ids_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id), mediafile_id integer NOT NULL REFERENCES mediafile_t (id), PRIMARY KEY (group_id, mediafile_id) ); -CREATE TABLE IF NOT EXISTS nm_group_mediafile_inherited_access_group_ids_mediafile_t ( +CREATE TABLE nm_group_mediafile_inherited_access_group_ids_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id), mediafile_id integer NOT NULL REFERENCES mediafile_t (id), PRIMARY KEY (group_id, mediafile_id) ); -CREATE TABLE IF NOT EXISTS nm_group_read_comment_section_ids_motion_comment_section_t ( +CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id), motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE IF NOT EXISTS nm_group_write_comment_section_ids_motion_comment_section_t ( +CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id), motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE IF NOT EXISTS nm_group_poll_ids_poll_t ( +CREATE TABLE nm_group_poll_ids_poll_t ( group_id integer NOT NULL REFERENCES group_t (id), poll_id integer NOT NULL REFERENCES poll_t (id), PRIMARY KEY (group_id, poll_id) ); -CREATE TABLE IF NOT EXISTS gm_tag_tagged_ids_t ( +CREATE TABLE gm_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, tag_id integer NOT NULL REFERENCES tag_t(id), tagged_id varchar(100) NOT NULL, @@ -1190,19 +1190,19 @@ CREATE TABLE IF NOT EXISTS gm_tag_tagged_ids_t ( CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); -CREATE TABLE IF NOT EXISTS nm_motion_all_derived_motion_ids_motion_t ( +CREATE TABLE nm_motion_all_derived_motion_ids_motion_t ( all_derived_motion_id integer NOT NULL REFERENCES motion_t (id), all_origin_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (all_derived_motion_id, all_origin_id) ); -CREATE TABLE IF NOT EXISTS nm_motion_identical_motion_ids_motion_t ( +CREATE TABLE nm_motion_identical_motion_ids_motion_t ( identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id), identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); -CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_ids_t ( +CREATE TABLE gm_motion_state_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), state_extension_reference_id varchar(100) NOT NULL, @@ -1211,7 +1211,7 @@ CREATE TABLE IF NOT EXISTS gm_motion_state_extension_reference_ids_t ( CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); -CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_ids_t ( +CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), recommendation_extension_reference_id varchar(100) NOT NULL, @@ -1220,19 +1220,19 @@ CREATE TABLE IF NOT EXISTS gm_motion_recommendation_extension_reference_ids_t ( CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); -CREATE TABLE IF NOT EXISTS nm_motion_state_next_state_ids_motion_state_t ( +CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( next_state_id integer NOT NULL REFERENCES motion_state_t (id), previous_state_id integer NOT NULL REFERENCES motion_state_t (id), PRIMARY KEY (next_state_id, previous_state_id) ); -CREATE TABLE IF NOT EXISTS nm_poll_voted_ids_user_t ( +CREATE TABLE nm_poll_voted_ids_user_t ( poll_id integer NOT NULL REFERENCES poll_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_ids_t ( +CREATE TABLE gm_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, mediafile_id integer NOT NULL REFERENCES mediafile_t(id), attachment_id varchar(100) NOT NULL, @@ -1243,20 +1243,20 @@ CREATE TABLE IF NOT EXISTS gm_mediafile_attachment_ids_t ( CONSTRAINT unique_$mediafile_id_$attachment_id UNIQUE (mediafile_id, attachment_id) ); -CREATE TABLE IF NOT EXISTS nm_chat_group_read_group_ids_group_t ( +CREATE TABLE nm_chat_group_read_group_ids_group_t ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id), group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); -CREATE TABLE IF NOT EXISTS nm_chat_group_write_group_ids_group_t ( +CREATE TABLE nm_chat_group_write_group_ids_group_t ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id), group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); -- View definitions -CREATE OR REPLACE VIEW organization AS SELECT *, +CREATE VIEW organization AS SELECT *, (select array_agg(c.id) from committee_t c where c.organization_id = o.id) as committee_ids, (select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, @@ -1268,7 +1268,7 @@ CREATE OR REPLACE VIEW organization AS SELECT *, FROM organization_t o; -CREATE OR REPLACE VIEW user_ AS SELECT *, +CREATE VIEW user_ AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, (select array_agg(n.committee_id) from nm_committee_user_ids_user_t n where n.user_id = u.id) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, @@ -1283,7 +1283,7 @@ FROM user_t u; comment on column user_.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; -CREATE OR REPLACE VIEW meeting_user AS SELECT *, +CREATE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, (select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.meeting_user_id = m.id) as supported_motion_ids, @@ -1298,17 +1298,17 @@ CREATE OR REPLACE VIEW meeting_user AS SELECT *, FROM meeting_user_t m; -CREATE OR REPLACE VIEW organization_tag AS SELECT *, +CREATE VIEW organization_tag AS SELECT *, (select array_agg(g.id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; -CREATE OR REPLACE VIEW theme AS SELECT *, +CREATE VIEW theme AS SELECT *, (select o.id from organization_t o where o.theme_id = t.id) as theme_for_organization_id FROM theme_t t; -CREATE OR REPLACE VIEW committee AS SELECT *, +CREATE VIEW committee AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, (select array_agg(n.user_id) from nm_committee_user_ids_user_t n where n.committee_id = c.id) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, @@ -1319,7 +1319,7 @@ FROM committee_t c; comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; -CREATE OR REPLACE VIEW meeting AS SELECT *, +CREATE VIEW meeting AS SELECT *, (select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, (select array_agg(p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, (select array_agg(p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, @@ -1383,13 +1383,13 @@ CREATE OR REPLACE VIEW meeting AS SELECT *, FROM meeting_t m; -CREATE OR REPLACE VIEW structure_level AS SELECT *, +CREATE VIEW structure_level AS SELECT *, (select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, (select array_agg(sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids FROM structure_level_t s; -CREATE OR REPLACE VIEW group_ AS SELECT *, +CREATE VIEW group_ AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, @@ -1405,36 +1405,36 @@ FROM group_t g; comment on column group_.mediafile_inherited_access_group_ids is 'Calculated field.'; -CREATE OR REPLACE VIEW tag AS SELECT *, +CREATE VIEW tag AS SELECT *, (select array_agg(g.id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids FROM tag_t t; -CREATE OR REPLACE VIEW agenda_item AS SELECT *, +CREATE VIEW agenda_item AS SELECT *, (select array_agg(ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, (select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_item_t a; -CREATE OR REPLACE VIEW list_of_speakers AS SELECT *, +CREATE VIEW list_of_speakers AS SELECT *, (select array_agg(s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, (select array_agg(s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids FROM list_of_speakers_t l; -CREATE OR REPLACE VIEW structure_level_list_of_speakers AS SELECT *, +CREATE VIEW structure_level_list_of_speakers AS SELECT *, (select array_agg(s1.id) from speaker_t s1 where s1.structure_level_list_of_speakers_id = s.id) as speaker_ids FROM structure_level_list_of_speakers_t s; -CREATE OR REPLACE VIEW point_of_order_category AS SELECT *, +CREATE VIEW point_of_order_category AS SELECT *, (select array_agg(s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids FROM point_of_order_category_t p; -CREATE OR REPLACE VIEW topic AS SELECT *, +CREATE VIEW topic AS SELECT *, (select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, @@ -1443,7 +1443,7 @@ CREATE OR REPLACE VIEW topic AS SELECT *, FROM topic_t t; -CREATE OR REPLACE VIEW motion AS SELECT *, +CREATE VIEW motion AS SELECT *, (select array_agg(m1.id) from motion_t m1 where m1.lead_motion_id = m.id) as amendment_ids, (select array_agg(m1.id) from motion_t m1 where m1.sort_parent_id = m.id) as sort_child_ids, (select array_agg(m1.id) from motion_t m1 where m1.origin_id = m.id) as derived_motion_ids, @@ -1471,20 +1471,20 @@ CREATE OR REPLACE VIEW motion AS SELECT *, FROM motion_t m; -CREATE OR REPLACE VIEW motion_comment_section AS SELECT *, +CREATE VIEW motion_comment_section AS SELECT *, (select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, (select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as read_group_ids, (select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as write_group_ids FROM motion_comment_section_t m; -CREATE OR REPLACE VIEW motion_category AS SELECT *, +CREATE VIEW motion_category AS SELECT *, (select array_agg(mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, (select array_agg(m1.id) from motion_t m1 where m1.category_id = m.id) as motion_ids FROM motion_category_t m; -CREATE OR REPLACE VIEW motion_block AS SELECT *, +CREATE VIEW motion_block AS SELECT *, (select array_agg(m1.id) from motion_t m1 where m1.block_id = m.id) as motion_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, @@ -1492,7 +1492,7 @@ CREATE OR REPLACE VIEW motion_block AS SELECT *, FROM motion_block_t m; -CREATE OR REPLACE VIEW motion_state AS SELECT *, +CREATE VIEW motion_state AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, (select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.previous_state_id = m.id) as next_state_ids, (select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.next_state_id = m.id) as previous_state_ids, @@ -1502,7 +1502,7 @@ CREATE OR REPLACE VIEW motion_state AS SELECT *, FROM motion_state_t m; -CREATE OR REPLACE VIEW motion_workflow AS SELECT *, +CREATE VIEW motion_workflow AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, (select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, (select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id, @@ -1510,12 +1510,12 @@ CREATE OR REPLACE VIEW motion_workflow AS SELECT *, FROM motion_workflow_t m; -CREATE OR REPLACE VIEW motion_statute_paragraph AS SELECT *, +CREATE VIEW motion_statute_paragraph AS SELECT *, (select array_agg(m1.id) from motion_t m1 where m1.statute_paragraph_id = m.id) as motion_ids FROM motion_statute_paragraph_t m; -CREATE OR REPLACE VIEW poll AS SELECT *, +CREATE VIEW poll AS SELECT *, (select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, (select array_agg(n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, @@ -1523,13 +1523,13 @@ CREATE OR REPLACE VIEW poll AS SELECT *, FROM poll_t p; -CREATE OR REPLACE VIEW option AS SELECT *, +CREATE VIEW option AS SELECT *, (select p.id from poll_t p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, (select array_agg(v.id) from vote_t v where v.option_id = o.id) as vote_ids FROM option_t o; -CREATE OR REPLACE VIEW assignment AS SELECT *, +CREATE VIEW assignment AS SELECT *, (select array_agg(ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, (select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, @@ -1540,13 +1540,13 @@ CREATE OR REPLACE VIEW assignment AS SELECT *, FROM assignment_t a; -CREATE OR REPLACE VIEW poll_candidate_list AS SELECT *, +CREATE VIEW poll_candidate_list AS SELECT *, (select array_agg(pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, (select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id FROM poll_candidate_list_t p; -CREATE OR REPLACE VIEW mediafile AS SELECT *, +CREATE VIEW mediafile AS SELECT *, (select array_agg(n.group_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as inherited_access_group_ids, (select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as access_group_ids, (select array_agg(m1.id) from mediafile_t m1 where m1.parent_id = m.id) as child_ids, @@ -1573,7 +1573,7 @@ FROM mediafile_t m; comment on column mediafile.inherited_access_group_ids is 'Calculated field.'; -CREATE OR REPLACE VIEW projector AS SELECT *, +CREATE VIEW projector AS SELECT *, (select array_agg(p1.id) from projection_t p1 where p1.current_projector_id = p.id) as current_projection_ids, (select array_agg(p1.id) from projection_t p1 where p1.preview_projector_id = p.id) as preview_projection_ids, (select array_agg(p1.id) from projection_t p1 where p1.history_projector_id = p.id) as history_projection_ids, @@ -1581,19 +1581,19 @@ CREATE OR REPLACE VIEW projector AS SELECT *, FROM projector_t p; -CREATE OR REPLACE VIEW projector_message AS SELECT *, +CREATE VIEW projector_message AS SELECT *, (select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_message_t p; -CREATE OR REPLACE VIEW projector_countdown AS SELECT *, +CREATE VIEW projector_countdown AS SELECT *, (select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, (select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, (select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdown_t p; -CREATE OR REPLACE VIEW chat_group AS SELECT *, +CREATE VIEW chat_group AS SELECT *, (select array_agg(cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, (select array_agg(n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, (select array_agg(n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 09c662bb..2865771f 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -10,12 +10,8 @@ from typing import Any, TypedDict, cast from helper_get_names import FieldSqlErrorType # type: ignore -from helper_get_names import ( - KEYSEPARATOR, - HelperGetNames, - InternalHelper, - TableFieldType, -) +from helper_get_names import (KEYSEPARATOR, HelperGetNames, InternalHelper, + TableFieldType) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() @@ -575,9 +571,9 @@ class Helper: """ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. - CREATE EXTENSION hstore IF NOT EXISTS; -- included in standard postgres-installations, check for alpine + CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine - create or replace function check_not_null_for_relation_lists() returns trigger as $not_null_trigger$ + CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name of field to check, usually a field in a view -- column_name of field to check @@ -617,7 +613,7 @@ class Helper: end; $not_null_trigger$ language plpgsql; - CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ + CREATE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ DECLARE statements CURSOR FOR SELECT tablename FROM pg_tables @@ -637,7 +633,7 @@ class Helper: INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE = string.Template( dedent( """ - CREATE TABLE IF NOT EXISTS ${table_name} ( + CREATE TABLE ${table_name} ( ${field1} integer NOT NULL REFERENCES ${table1} (id), ${field2} integer NOT NULL REFERENCES ${table2} (id), PRIMARY KEY (${list_of_keys}) @@ -648,7 +644,7 @@ class Helper: INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE = string.Template( dedent( """ - CREATE TABLE IF NOT EXISTS ${table_name} ( + CREATE TABLE ${table_name} ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}), ${own_table_column} varchar(100) NOT NULL, @@ -706,7 +702,7 @@ def get_table_letter(table_name: str, letters: list[str] = []) -> str: @staticmethod def get_table_head(table_name: str) -> str: - return f"\nCREATE TABLE IF NOT EXISTS {HelperGetNames.get_table_name(table_name)} (\n" + return f"\nCREATE TABLE {HelperGetNames.get_table_name(table_name)} (\n" @staticmethod def get_table_body_end(code: str) -> str: @@ -716,7 +712,7 @@ def get_table_body_end(code: str) -> str: @staticmethod def get_view_head(table_name: str) -> str: - return f"\nCREATE OR REPLACE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" + return f"\nCREATE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" @staticmethod def get_view_body_end(table_name: str, code: str) -> str: From 70d4e8f46c936d67af4c9b37c472c2428dffd7dd Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 16 Aug 2024 11:51:25 +0200 Subject: [PATCH 067/142] fix formatting --- dev/src/generate_sql_schema.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 2865771f..c006d67c 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -10,8 +10,12 @@ from typing import Any, TypedDict, cast from helper_get_names import FieldSqlErrorType # type: ignore -from helper_get_names import (KEYSEPARATOR, HelperGetNames, InternalHelper, - TableFieldType) +from helper_get_names import ( + KEYSEPARATOR, + HelperGetNames, + InternalHelper, + TableFieldType, +) SOURCE = (Path(__file__).parent / ".." / ".." / "models.yml").resolve() DESTINATION = (Path(__file__).parent / ".." / "sql" / "schema_relational.sql").resolve() @@ -712,7 +716,9 @@ def get_table_body_end(code: str) -> str: @staticmethod def get_view_head(table_name: str) -> str: - return f"\nCREATE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" + return ( + f"\nCREATE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" + ) @staticmethod def get_view_body_end(table_name: str, code: str) -> str: From 7d87d03c06ba9a5baeec273ef4a3d85296729e13 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Thu, 22 Aug 2024 13:49:37 +0200 Subject: [PATCH 068/142] version upgrade python-sql package to 1.5.1 --- dev/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/requirements.txt b/dev/requirements.txt index bb6ca5e0..17cde43d 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -10,7 +10,7 @@ simplejson==3.19.2 debugpy==1.8.0 requests==2.31.0 psycopg[binary]==3.1.18 -python-sql==1.4.3 +python-sql==1.5.1 # typing types-PyYAML==6.0.12.20240808 From ab81ad9e9ce72006703008db0589ae2d91d0a40c Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 28 Aug 2024 10:29:02 +0200 Subject: [PATCH 069/142] Remove function truncate_tables from relational schema generation --- dev/sql/schema_relational.sql | 13 ------------- dev/src/generate_sql_schema.py | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 8c35948d..e7020dd9 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -42,19 +42,6 @@ begin RETURN NULL; -- AFTER TRIGGER needs no return end; $not_null_trigger$ language plpgsql; - -CREATE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ -DECLARE - statements CURSOR FOR - SELECT tablename FROM pg_tables - WHERE tableowner = username AND schemaname = 'public'; -BEGIN - FOR stmt IN statements LOOP - EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' RESTART IDENTITY CASCADE;'; - END LOOP; -END; -$$ LANGUAGE plpgsql; - -- MODELS_YML_CHECKSUM = 'e3bcb2c45850469b5e56d3542d12ea15' -- Type definitions diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index c006d67c..91c4b3fd 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -616,19 +616,6 @@ class Helper: RETURN NULL; -- AFTER TRIGGER needs no return end; $not_null_trigger$ language plpgsql; - - CREATE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$ - DECLARE - statements CURSOR FOR - SELECT tablename FROM pg_tables - WHERE tableowner = username AND schemaname = 'public'; - BEGIN - FOR stmt IN statements LOOP - EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' RESTART IDENTITY CASCADE;'; - END LOOP; - END; - $$ LANGUAGE plpgsql; - """ ) FIELD_TEMPLATE = string.Template( From 694aaa8aa437541a8743687d2e8ed07469feddb1 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 4 Sep 2024 10:42:53 +0200 Subject: [PATCH 070/142] set all FK to INITIALLY DEFERRED --- dev/sql/schema_relational.sql | 268 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 9 +- 2 files changed, 139 insertions(+), 138 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index e7020dd9..0d93a7a9 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1589,44 +1589,44 @@ FROM chat_group_t c; -- Alter table relations ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); -ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); -ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id); +ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id); -ALTER TABLE committee_t ADD FOREIGN KEY(forwarding_user_id) REFERENCES user_t(id); +ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE committee_t ADD FOREIGN KEY(forwarding_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES organization_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafile_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(committee_id) REFERENCES committee_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(reference_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdown_t(id); -ALTER TABLE meeting_t ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdown_t(id); +ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(default_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(admin_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(anonymous_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; -ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE group_t ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE group_t ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -1634,118 +1634,118 @@ ALTER TABLE group_t ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES me ALTER TABLE group_t ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); -ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id); -ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id); -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id); -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id); -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id); -ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id); -ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id); -ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_t ADD FOREIGN KEY(lead_motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(sort_parent_id) REFERENCES motion_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(origin_id) REFERENCES motion_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(origin_meeting_id) REFERENCES meeting_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(state_id) REFERENCES motion_state_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(recommendation_id) REFERENCES motion_state_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraph_t(id); -ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_t ADD FOREIGN KEY(lead_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(sort_parent_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(origin_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(origin_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(recommendation_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraph_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_comment_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_comment_t ADD FOREIGN KEY(section_id) REFERENCES motion_comment_section_t(id); -ALTER TABLE motion_comment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_comment_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_comment_t ADD FOREIGN KEY(section_id) REFERENCES motion_comment_section_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_comment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_comment_section_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_comment_section_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id); -ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id); -ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_state_t ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_state_t(id); +ALTER TABLE motion_state_t ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_state_t ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_statute_paragraph_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE motion_statute_paragraph_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); -ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id); -ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id); -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id); -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id); -ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id); -ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); -ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id); -ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id); -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id); -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id); -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id); -ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id); -ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id); +ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -1763,31 +1763,31 @@ ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_pol ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id); -ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); - -ALTER TABLE projector_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); - -ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); - -ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); - -ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id); -ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id); -ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id); +ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE projector_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -- Create trigger diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 91c4b3fd..42d9a33f 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -954,10 +954,11 @@ def _first_to_second(t1: str, t2: str) -> bool: if ftable == t2: return True return False - - if _first_to_second(own_table, foreign_table): - return _first_to_second(foreign_table, own_table) - return False + return True + # TODO: Will be reverted in a future issue + # if _first_to_second(own_table, foreign_table): + # return _first_to_second(foreign_table, own_table) + # return False @staticmethod def get_foreign_table_from_to_or_reference( From eaa65197edb1cab5419a5afd8787ed0bfeb82147 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 4 Sep 2024 10:45:47 +0200 Subject: [PATCH 071/142] cleanup --- dev/src/generate_sql_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 42d9a33f..b52b25b3 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -954,6 +954,7 @@ def _first_to_second(t1: str, t2: str) -> bool: if ftable == t2: return True return False + return True # TODO: Will be reverted in a future issue # if _first_to_second(own_table, foreign_table): From 0461b60a68993046b9bb4805f3cf1660b249f199 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 18 Sep 2024 12:04:31 +0200 Subject: [PATCH 072/142] main merge with fixes, generate field_name for nm-table_name from initials, if > 25 chars --- dev/src/helper_get_names.py | 18 +++++++++++++----- models.yml | 10 ++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index e13735bd..ffbff67a 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -76,6 +76,16 @@ def wrapper(*args, **kwargs) -> str: # type:ignore return wrapper + @staticmethod + def get_initial_letters(words: str) -> str: + """get only the initial letters of words, separated by _, + if the given string has more than 25 letters + """ + if len(words) > 25: + return "".join([word[0] for word in words.split("_")]) + else: + return words + @staticmethod @max_length def get_table_name(table_name: str) -> str: @@ -94,12 +104,10 @@ def get_view_name(table_name: str) -> str: @max_length def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: """get's the table name n:m-relations intermediate table""" - if (own_str := f"{own.table}_{own.column}") < ( - foreign_str := f"{foreign.table}_{foreign.column}" - ): - return f"nm_{own_str}_{foreign.table}" + if (f"{own.table}_{own.column}") < (f"{foreign.table}_{foreign.column}"): + return f"nm_{own.table}_{HelperGetNames.get_initial_letters(own.column)}_{foreign.table}" else: - return f"nm_{foreign_str}_{own.table}" + return f"nm_{foreign.table}_{HelperGetNames.get_initial_letters(foreign.column)}_{own.table}" @staticmethod @max_length diff --git a/models.yml b/models.yml index 11adbe9b..1d73d918 100644 --- a/models.yml +++ b/models.yml @@ -2337,7 +2337,7 @@ list_of_speakers: - motion_block - assignment - topic - - mediafile + - meeting_mediafile to: collections: - motion @@ -3929,6 +3929,7 @@ mediafile: published_to_meetings_in_organization_id: type: relation to: organization/published_mediafile_ids + reference: organization restriction_mode: A parent_id: type: relation @@ -3946,6 +3947,9 @@ mediafile: to: - meeting/mediafile_ids - organization/mediafile_ids + reference: + - meeting + - organization restriction_mode: A required: true constant: true @@ -3963,11 +3967,13 @@ meeting_mediafile: mediafile_id: type: relation to: mediafile/meeting_mediafile_ids + reference: mediafile required: true restriction_mode: A meeting_id: type: relation to: meeting/meeting_mediafile_ids + reference: meeting required: true restriction_mode: A is_public: @@ -4318,7 +4324,7 @@ projection: reference: - meeting - motion - - mediafile + - meeting_mediafile - list_of_speakers - motion_block - assignment From 163343ef802e4a92af09499e4b951a7ada950e56 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 18 Sep 2024 12:26:28 +0200 Subject: [PATCH 073/142] newly generated schema_relational.sql --- dev/sql/schema_relational.sql | 222 +++++++++++++++++++--------------- 1 file changed, 126 insertions(+), 96 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 0d93a7a9..9afee152 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -42,7 +42,7 @@ begin RETURN NULL; -- AFTER TRIGGER needs no return end; $not_null_trigger$ language plpgsql; --- MODELS_YML_CHECKSUM = 'e3bcb2c45850469b5e56d3542d12ea15' +-- MODELS_YML_CHECKSUM = 'ff3a9aa7702358f023a5184aec6536d5' -- Type definitions -- Table definitions @@ -105,7 +105,7 @@ CREATE TABLE user_t ( title varchar(256), first_name varchar(256), last_name varchar(256), - is_active boolean, + is_active boolean DEFAULT True, is_physical_person boolean DEFAULT True, password varchar(256), default_password varchar(256), @@ -337,6 +337,7 @@ CREATE TABLE meeting_t ( motion_poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_motion_poll_ballot_paper_selection CHECK (motion_poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')) DEFAULT 'CUSTOM_NUMBER', motion_poll_ballot_paper_number integer DEFAULT 8, motion_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', + motion_poll_default_method varchar(256) DEFAULT 'YNA', motion_poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_motion_poll_default_onehundred_percent_base CHECK (motion_poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', motion_poll_default_backend varchar(256) CONSTRAINT enum_meeting_motion_poll_default_backend CHECK (motion_poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', users_enable_presence_view boolean DEFAULT False, @@ -435,7 +436,7 @@ CREATE TABLE group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, external_id varchar(256), name varchar(256) NOT NULL, - permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update']::varchar[]), + permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_see_origin', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update']::varchar[]), weight integer, used_as_motion_poll_default_id integer, used_as_assignment_poll_default_id integer, @@ -511,8 +512,8 @@ CREATE TABLE list_of_speakers_t ( content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','mediafile')), + content_object_id_meeting_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','meeting_mediafile')), meeting_id integer NOT NULL ); @@ -918,8 +919,8 @@ CREATE TABLE mediafile_t ( mimetype varchar(256), pdf_information jsonb, create_timestamp timestamptz, - is_public boolean NOT NULL, token varchar(256), + published_to_meetings_in_organization_id integer, parent_id integer, owner_id varchar(100) NOT NULL, owner_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'meeting' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -932,7 +933,18 @@ CREATE TABLE mediafile_t ( comment on column mediafile_t.title is 'Title and parent_id must be unique.'; comment on column mediafile_t.filesize is 'In bytes, not the human readable format anymore.'; comment on column mediafile_t.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; -comment on column mediafile_t.is_public is 'Calculated field. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; + + +CREATE TABLE meeting_mediafile_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + mediafile_id integer NOT NULL, + meeting_id integer NOT NULL, + is_public boolean NOT NULL +); + + + +comment on column meeting_mediafile_t.is_public is 'Calculated in actions. Used to discern whether the (meeting-)mediafile can be seen by everyone, because, in the case of inherited_access_group_ids == [], it would otherwise not be clear. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; CREATE TABLE projector_t ( @@ -992,7 +1004,7 @@ CREATE TABLE projection_t ( content_object_id varchar(100) NOT NULL, content_object_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_meeting_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_list_of_speakers_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'list_of_speakers' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -1001,7 +1013,7 @@ CREATE TABLE projection_t ( content_object_id_poll_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_projector_message_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_message' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_projector_countdown_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_countdown' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('meeting','motion','mediafile','list_of_speakers','motion_block','assignment','agenda_item','topic','poll','projector_message','projector_countdown')), + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('meeting','motion','meeting_mediafile','list_of_speakers','motion_block','assignment','agenda_item','topic','poll','projector_message','projector_countdown')), meeting_id integer NOT NULL ); @@ -1050,7 +1062,7 @@ CREATE TABLE chat_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, content text NOT NULL, created timestamptz NOT NULL, - meeting_user_id integer NOT NULL, + meeting_user_id integer, chat_group_id integer NOT NULL, meeting_id integer NOT NULL ); @@ -1136,16 +1148,16 @@ CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( PRIMARY KEY (group_id, meeting_user_id) ); -CREATE TABLE nm_group_mediafile_access_group_ids_mediafile_t ( +CREATE TABLE nm_group_mmagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id), - mediafile_id integer NOT NULL REFERENCES mediafile_t (id), - PRIMARY KEY (group_id, mediafile_id) + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), + PRIMARY KEY (group_id, meeting_mediafile_id) ); -CREATE TABLE nm_group_mediafile_inherited_access_group_ids_mediafile_t ( +CREATE TABLE nm_group_mmiagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id), - mediafile_id integer NOT NULL REFERENCES mediafile_t (id), - PRIMARY KEY (group_id, mediafile_id) + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), + PRIMARY KEY (group_id, meeting_mediafile_id) ); CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section_t ( @@ -1219,15 +1231,15 @@ CREATE TABLE nm_poll_voted_ids_user_t ( PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE gm_mediafile_attachment_ids_t ( +CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - mediafile_id integer NOT NULL REFERENCES mediafile_t(id), + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id), attachment_id varchar(100) NOT NULL, attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id), attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id), CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), - CONSTRAINT unique_$mediafile_id_$attachment_id UNIQUE (mediafile_id, attachment_id) + CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); CREATE TABLE nm_chat_group_read_group_ids_group_t ( @@ -1251,6 +1263,7 @@ CREATE VIEW organization AS SELECT *, (select array_agg(ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, (select array_agg(t.id) from theme_t t where t.organization_id = o.id) as theme_ids, (select array_agg(m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(m.id) from mediafile_t m where m.published_to_meetings_in_organization_id = o.id) as published_mediafile_ids, (select array_agg(u.id) from user_t u where u.organization_id = o.id) as user_ids FROM organization_t o; @@ -1326,6 +1339,7 @@ CREATE VIEW meeting AS SELECT *, (select array_agg(s.id) from speaker_t s where s.meeting_id = m.id) as speaker_ids, (select array_agg(t.id) from topic_t t where t.meeting_id = m.id) as topic_ids, (select array_agg(g.id) from group_t g where g.meeting_id = m.id) as group_ids, +(select array_agg(mm.id) from meeting_mediafile_t mm where mm.meeting_id = m.id) as meeting_mediafile_ids, (select array_agg(m1.id) from mediafile_t m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, (select array_agg(m1.id) from motion_t m1 where m1.meeting_id = m.id) as motion_ids, (select array_agg(m1.id) from motion_t m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, @@ -1381,8 +1395,8 @@ CREATE VIEW group_ AS SELECT *, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, -(select array_agg(n.mediafile_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_access_group_ids, -(select array_agg(n.mediafile_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.group_id = g.id) as mediafile_inherited_access_group_ids, +(select array_agg(n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_access_group_ids, +(select array_agg(n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, (select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, (select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, (select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, @@ -1390,7 +1404,7 @@ CREATE VIEW group_ AS SELECT *, (select array_agg(n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids FROM group_t g; -comment on column group_.mediafile_inherited_access_group_ids is 'Calculated field.'; +comment on column group_.meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; CREATE VIEW tag AS SELECT *, (select array_agg(g.id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids @@ -1422,7 +1436,7 @@ FROM point_of_order_category_t p; CREATE VIEW topic AS SELECT *, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, (select array_agg(p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, @@ -1452,7 +1466,7 @@ CREATE VIEW motion AS SELECT *, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, (select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, (select array_agg(p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids FROM motion_t m; @@ -1522,7 +1536,7 @@ CREATE VIEW assignment AS SELECT *, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, (select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.mediafile_id) from gm_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids FROM assignment_t a; @@ -1534,12 +1548,17 @@ FROM poll_candidate_list_t p; CREATE VIEW mediafile AS SELECT *, -(select array_agg(n.group_id) from nm_group_mediafile_inherited_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id) from nm_group_mediafile_access_group_ids_mediafile_t n where n.mediafile_id = m.id) as access_group_ids, (select array_agg(m1.id) from mediafile_t m1 where m1.parent_id = m.id) as child_ids, -(select l.id from list_of_speakers_t l where l.content_object_id_mediafile_id = m.id) as list_of_speakers_id, -(select array_agg(p.id) from projection_t p where p.content_object_id_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_mediafile_attachment_ids_t g where g.mediafile_id = m.id) as attachment_ids, +(select array_agg(mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids +FROM mediafile_t m; + + +CREATE VIEW meeting_mediafile AS SELECT *, +(select array_agg(n.group_id) from nm_group_mmiagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as access_group_ids, +(select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, +(select array_agg(p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, +(select array_agg(g.id) from gm_meeting_mediafile_attachment_ids_t g where g.meeting_mediafile_id = m.id) as attachment_ids, (select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, @@ -1556,9 +1575,9 @@ CREATE VIEW mediafile AS SELECT *, (select m1.id from meeting_t m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, (select m1.id from meeting_t m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, (select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id -FROM mediafile_t m; +FROM meeting_mediafile_t m; -comment on column mediafile.inherited_access_group_ids is 'Calculated field.'; +comment on column meeting_mediafile.inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; CREATE VIEW projector AS SELECT *, (select array_agg(p1.id) from projection_t p1 where p1.current_projector_id = p.id) as current_projection_ids, @@ -1602,22 +1621,22 @@ ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES o ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(reference_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; @@ -1651,7 +1670,7 @@ ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFE ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id) INITIALLY DEFERRED; @@ -1743,10 +1762,14 @@ ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_list_of_speakers_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -1768,7 +1791,7 @@ ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projec ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; @@ -1931,6 +1954,7 @@ SQL nr:1rR => organization/organization_tag_ids:-> organization_tag/organization FIELD 1rR:1t => organization/theme_id:-> theme/theme_for_organization_id SQL nr:1rR => organization/theme_ids:-> theme/organization_id SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id +SQL nt:1r => organization/published_mediafile_ids:-> mediafile/published_to_meetings_in_organization_id SQL nr:1rR => organization/user_ids:-> user/organization_id SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids @@ -1955,7 +1979,7 @@ SQL nt:1rR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id -SQL nt:1rR => meeting_user/chat_message_ids:-> chat_message/meeting_user_id +SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids @@ -1997,6 +2021,7 @@ SQL nt:1rR => meeting/point_of_order_category_ids:-> point_of_order_category/mee SQL nt:1rR => meeting/speaker_ids:-> speaker/meeting_id SQL nt:1rR => meeting/topic_ids:-> topic/meeting_id SQL nt:1rR => meeting/group_ids:-> group/meeting_id +SQL nt:1rR => meeting/meeting_mediafile_ids:-> meeting_mediafile/meeting_id SQL nt:1GrR => meeting/mediafile_ids:-> mediafile/owner_id SQL nt:1rR => meeting/motion_ids:-> motion/meeting_id SQL nt:1r => meeting/forwarded_motion_ids:-> motion/origin_meeting_id @@ -2020,22 +2045,22 @@ SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id SQL nt:1rR => meeting/chat_group_ids:-> chat_group/meeting_id SQL nt:1rR => meeting/chat_message_ids:-> chat_message/meeting_id SQL nt:1rR => meeting/structure_level_ids:-> structure_level/meeting_id -FIELD 1r:1t => meeting/logo_projector_main_id:-> mediafile/used_as_logo_projector_main_in_meeting_id -FIELD 1r:1t => meeting/logo_projector_header_id:-> mediafile/used_as_logo_projector_header_in_meeting_id -FIELD 1r:1t => meeting/logo_web_header_id:-> mediafile/used_as_logo_web_header_in_meeting_id -FIELD 1r:1t => meeting/logo_pdf_header_l_id:-> mediafile/used_as_logo_pdf_header_l_in_meeting_id -FIELD 1r:1t => meeting/logo_pdf_header_r_id:-> mediafile/used_as_logo_pdf_header_r_in_meeting_id -FIELD 1r:1t => meeting/logo_pdf_footer_l_id:-> mediafile/used_as_logo_pdf_footer_l_in_meeting_id -FIELD 1r:1t => meeting/logo_pdf_footer_r_id:-> mediafile/used_as_logo_pdf_footer_r_in_meeting_id -FIELD 1r:1t => meeting/logo_pdf_ballot_paper_id:-> mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id -FIELD 1r:1t => meeting/font_regular_id:-> mediafile/used_as_font_regular_in_meeting_id -FIELD 1r:1t => meeting/font_italic_id:-> mediafile/used_as_font_italic_in_meeting_id -FIELD 1r:1t => meeting/font_bold_id:-> mediafile/used_as_font_bold_in_meeting_id -FIELD 1r:1t => meeting/font_bold_italic_id:-> mediafile/used_as_font_bold_italic_in_meeting_id -FIELD 1r:1t => meeting/font_monospace_id:-> mediafile/used_as_font_monospace_in_meeting_id -FIELD 1r:1t => meeting/font_chyron_speaker_name_id:-> mediafile/used_as_font_chyron_speaker_name_in_meeting_id -FIELD 1r:1t => meeting/font_projector_h1_id:-> mediafile/used_as_font_projector_h1_in_meeting_id -FIELD 1r:1t => meeting/font_projector_h2_id:-> mediafile/used_as_font_projector_h2_in_meeting_id +FIELD 1r:1t => meeting/logo_projector_main_id:-> meeting_mediafile/used_as_logo_projector_main_in_meeting_id +FIELD 1r:1t => meeting/logo_projector_header_id:-> meeting_mediafile/used_as_logo_projector_header_in_meeting_id +FIELD 1r:1t => meeting/logo_web_header_id:-> meeting_mediafile/used_as_logo_web_header_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_header_l_id:-> meeting_mediafile/used_as_logo_pdf_header_l_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_header_r_id:-> meeting_mediafile/used_as_logo_pdf_header_r_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_footer_l_id:-> meeting_mediafile/used_as_logo_pdf_footer_l_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_footer_r_id:-> meeting_mediafile/used_as_logo_pdf_footer_r_in_meeting_id +FIELD 1r:1t => meeting/logo_pdf_ballot_paper_id:-> meeting_mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id +FIELD 1r:1t => meeting/font_regular_id:-> meeting_mediafile/used_as_font_regular_in_meeting_id +FIELD 1r:1t => meeting/font_italic_id:-> meeting_mediafile/used_as_font_italic_in_meeting_id +FIELD 1r:1t => meeting/font_bold_id:-> meeting_mediafile/used_as_font_bold_in_meeting_id +FIELD 1r:1t => meeting/font_bold_italic_id:-> meeting_mediafile/used_as_font_bold_italic_in_meeting_id +FIELD 1r:1t => meeting/font_monospace_id:-> meeting_mediafile/used_as_font_monospace_in_meeting_id +FIELD 1r:1t => meeting/font_chyron_speaker_name_id:-> meeting_mediafile/used_as_font_chyron_speaker_name_in_meeting_id +FIELD 1r:1t => meeting/font_projector_h1_id:-> meeting_mediafile/used_as_font_projector_h1_in_meeting_id +FIELD 1r:1t => meeting/font_projector_h2_id:-> meeting_mediafile/used_as_font_projector_h2_in_meeting_id FIELD 1rR:nt => meeting/committee_id:-> committee/meeting_ids SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids @@ -2070,8 +2095,8 @@ SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id SQL 1t:1r => group/anonymous_group_for_meeting_id:-> meeting/anonymous_group_id -SQL nt:nt => group/mediafile_access_group_ids:-> mediafile/access_group_ids -SQL nt:nt => group/mediafile_inherited_access_group_ids:-> mediafile/inherited_access_group_ids +SQL nt:nt => group/meeting_mediafile_access_group_ids:-> meeting_mediafile/access_group_ids +SQL nt:nt => group/meeting_mediafile_inherited_access_group_ids:-> meeting_mediafile/inherited_access_group_ids SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_group_ids SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids @@ -2097,7 +2122,7 @@ SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id FIELD 1rR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids -FIELD 1GrR:,,,, => list_of_speakers/content_object_id:-> motion/,motion_block/,assignment/,topic/,mediafile/ +FIELD 1GrR:,,,, => list_of_speakers/content_object_id:-> motion/,motion_block/,assignment/,topic/,meeting_mediafile/ SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id @@ -2117,7 +2142,7 @@ FIELD 1r:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids FIELD 1r:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids FIELD 1rR:nt => speaker/meeting_id:-> meeting/speaker_ids -SQL nt:nGt => topic/attachment_ids:-> mediafile/attachment_ids +SQL nt:nGt => topic/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids SQL 1tR:1GrR => topic/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:1GrR => topic/poll_ids:-> poll/content_object_id @@ -2154,7 +2179,7 @@ SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:nGt => motion/tag_ids:-> tag/tagged_ids -SQL nt:nGt => motion/attachment_ids:-> mediafile/attachment_ids +SQL nt:nGt => motion/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids SQL nt:1GrR => motion/projection_ids:-> projection/content_object_id SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id FIELD 1rR:nt => motion/meeting_id:-> meeting/motion_ids @@ -2238,7 +2263,7 @@ SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id SQL 1t:1GrR => assignment/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids -SQL nt:nGt => assignment/attachment_ids:-> mediafile/attachment_ids +SQL nt:nGt => assignment/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id FIELD 1rR:nt => assignment/meeting_id:-> meeting/assignment_ids @@ -2254,30 +2279,35 @@ FIELD 1rR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/pol FIELD 1r:nt => poll_candidate/user_id:-> user/poll_candidate_ids FIELD 1rR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids -SQL nt:nt => mediafile/inherited_access_group_ids:-> group/mediafile_inherited_access_group_ids -SQL nt:nt => mediafile/access_group_ids:-> group/mediafile_access_group_ids +FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id -SQL 1t:1GrR => mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GrR => mediafile/projection_ids:-> projection/content_object_id -SQL nGt:nt,nt,nt => mediafile/attachment_ids:-> motion/attachment_ids,topic/attachment_ids,assignment/attachment_ids FIELD 1GrR:, => mediafile/owner_id:-> meeting/,organization/ -SQL 1t:1r => mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id -SQL 1t:1r => mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id -SQL 1t:1r => mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id -SQL 1t:1r => mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id -SQL 1t:1r => mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id -SQL 1t:1r => mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id -SQL 1t:1r => mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id -SQL 1t:1r => mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id -SQL 1t:1r => mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id -SQL 1t:1r => mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id -SQL 1t:1r => mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id -SQL 1t:1r => mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id -SQL 1t:1r => mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id -SQL 1t:1r => mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id -SQL 1t:1r => mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id -SQL 1t:1r => mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id +SQL nt:1rR => mediafile/meeting_mediafile_ids:-> meeting_mediafile/mediafile_id + +FIELD 1rR:nt => meeting_mediafile/mediafile_id:-> mediafile/meeting_mediafile_ids +FIELD 1rR:nt => meeting_mediafile/meeting_id:-> meeting/meeting_mediafile_ids +SQL nt:nt => meeting_mediafile/inherited_access_group_ids:-> group/meeting_mediafile_inherited_access_group_ids +SQL nt:nt => meeting_mediafile/access_group_ids:-> group/meeting_mediafile_access_group_ids +SQL 1t:1GrR => meeting_mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => meeting_mediafile/projection_ids:-> projection/content_object_id +SQL nGt:nt,nt,nt => meeting_mediafile/attachment_ids:-> motion/attachment_meeting_mediafile_ids,topic/attachment_meeting_mediafile_ids,assignment/attachment_meeting_mediafile_ids +SQL 1t:1r => meeting_mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id +SQL 1t:1r => meeting_mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id +SQL 1t:1r => meeting_mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id +SQL 1t:1r => meeting_mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id +SQL 1t:1r => meeting_mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id +SQL 1t:1r => meeting_mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id +SQL 1t:1r => meeting_mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id +SQL 1t:1r => meeting_mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id +SQL 1t:1r => meeting_mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id +SQL 1t:1r => meeting_mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id +SQL 1t:1r => meeting_mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id @@ -2302,7 +2332,7 @@ FIELD 1rR:nt => projector/meeting_id:-> meeting/projector_ids FIELD 1r:nt => projection/current_projector_id:-> projector/current_projection_ids FIELD 1r:nt => projection/preview_projector_id:-> projector/preview_projection_ids FIELD 1r:nt => projection/history_projector_id:-> projector/history_projection_ids -FIELD 1GrR:,,,,,,,,,, => projection/content_object_id:-> meeting/,motion/,mediafile/,list_of_speakers/,motion_block/,assignment/,agenda_item/,topic/,poll/,projector_message/,projector_countdown/ +FIELD 1GrR:,,,,,,,,,, => projection/content_object_id:-> meeting/,motion/,meeting_mediafile/,list_of_speakers/,motion_block/,assignment/,agenda_item/,topic/,poll/,projector_message/,projector_countdown/ FIELD 1rR:nt => projection/meeting_id:-> meeting/all_projection_ids SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id @@ -2318,7 +2348,7 @@ SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids FIELD 1rR:nt => chat_group/meeting_id:-> meeting/chat_group_ids -FIELD 1rR:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids +FIELD 1r:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids FIELD 1rR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids From f87debbc7bc0204332cb1902fd56b2399ffb8b87 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Wed, 18 Sep 2024 17:28:59 +0200 Subject: [PATCH 074/142] remove tailing _t from nm- and gm-table names in schema generation --- dev/sql/schema_relational.sql | 184 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 13 ++- 2 files changed, 100 insertions(+), 97 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 9afee152..a19ca40e 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1096,19 +1096,19 @@ CREATE TABLE import_preview_t ( -- Intermediate table definitions -CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( +CREATE TABLE nm_meeting_user_supported_motion_ids_motion ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), motion_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (meeting_user_id, motion_id) ); -CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( +CREATE TABLE nm_meeting_user_structure_level_ids_structure_level ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), structure_level_id integer NOT NULL REFERENCES structure_level_t (id), PRIMARY KEY (meeting_user_id, structure_level_id) ); -CREATE TABLE gm_organization_tag_tagged_ids_t ( +CREATE TABLE gm_organization_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id), tagged_id varchar(100) NOT NULL, @@ -1118,67 +1118,67 @@ CREATE TABLE gm_organization_tag_tagged_ids_t ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE nm_committee_user_ids_user_t ( +CREATE TABLE nm_committee_user_ids_user ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE nm_committee_manager_ids_user_t ( +CREATE TABLE nm_committee_manager_ids_user ( committee_id integer NOT NULL REFERENCES committee_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE nm_committee_forward_to_committee_ids_committee_t ( +CREATE TABLE nm_committee_forward_to_committee_ids_committee ( forward_to_committee_id integer NOT NULL REFERENCES committee_t (id), receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id), PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); -CREATE TABLE nm_meeting_present_user_ids_user_t ( +CREATE TABLE nm_meeting_present_user_ids_user ( meeting_id integer NOT NULL REFERENCES meeting_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (meeting_id, user_id) ); -CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( +CREATE TABLE nm_group_meeting_user_ids_meeting_user ( group_id integer NOT NULL REFERENCES group_t (id), meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), PRIMARY KEY (group_id, meeting_user_id) ); -CREATE TABLE nm_group_mmagi_meeting_mediafile_t ( +CREATE TABLE nm_group_mmagi_meeting_mediafile ( group_id integer NOT NULL REFERENCES group_t (id), meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), PRIMARY KEY (group_id, meeting_mediafile_id) ); -CREATE TABLE nm_group_mmiagi_meeting_mediafile_t ( +CREATE TABLE nm_group_mmiagi_meeting_mediafile ( group_id integer NOT NULL REFERENCES group_t (id), meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), PRIMARY KEY (group_id, meeting_mediafile_id) ); -CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section_t ( +CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section ( group_id integer NOT NULL REFERENCES group_t (id), motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section_t ( +CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section ( group_id integer NOT NULL REFERENCES group_t (id), motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE nm_group_poll_ids_poll_t ( +CREATE TABLE nm_group_poll_ids_poll ( group_id integer NOT NULL REFERENCES group_t (id), poll_id integer NOT NULL REFERENCES poll_t (id), PRIMARY KEY (group_id, poll_id) ); -CREATE TABLE gm_tag_tagged_ids_t ( +CREATE TABLE gm_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, tag_id integer NOT NULL REFERENCES tag_t(id), tagged_id varchar(100) NOT NULL, @@ -1189,19 +1189,19 @@ CREATE TABLE gm_tag_tagged_ids_t ( CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); -CREATE TABLE nm_motion_all_derived_motion_ids_motion_t ( +CREATE TABLE nm_motion_all_derived_motion_ids_motion ( all_derived_motion_id integer NOT NULL REFERENCES motion_t (id), all_origin_id integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (all_derived_motion_id, all_origin_id) ); -CREATE TABLE nm_motion_identical_motion_ids_motion_t ( +CREATE TABLE nm_motion_identical_motion_ids_motion ( identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id), identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id), PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); -CREATE TABLE gm_motion_state_extension_reference_ids_t ( +CREATE TABLE gm_motion_state_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), state_extension_reference_id varchar(100) NOT NULL, @@ -1210,7 +1210,7 @@ CREATE TABLE gm_motion_state_extension_reference_ids_t ( CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); -CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( +CREATE TABLE gm_motion_recommendation_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id), recommendation_extension_reference_id varchar(100) NOT NULL, @@ -1219,19 +1219,19 @@ CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); -CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( +CREATE TABLE nm_motion_state_next_state_ids_motion_state ( next_state_id integer NOT NULL REFERENCES motion_state_t (id), previous_state_id integer NOT NULL REFERENCES motion_state_t (id), PRIMARY KEY (next_state_id, previous_state_id) ); -CREATE TABLE nm_poll_voted_ids_user_t ( +CREATE TABLE nm_poll_voted_ids_user ( poll_id integer NOT NULL REFERENCES poll_t (id), user_id integer NOT NULL REFERENCES user_t (id), PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( +CREATE TABLE gm_meeting_mediafile_attachment_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id), attachment_id varchar(100) NOT NULL, @@ -1242,13 +1242,13 @@ CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); -CREATE TABLE nm_chat_group_read_group_ids_group_t ( +CREATE TABLE nm_chat_group_read_group_ids_group ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id), group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); -CREATE TABLE nm_chat_group_write_group_ids_group_t ( +CREATE TABLE nm_chat_group_write_group_ids_group ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id), group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) @@ -1269,12 +1269,12 @@ FROM organization_t o; CREATE VIEW user_ AS SELECT *, -(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(n.committee_id) from nm_committee_user_ids_user_t n where n.user_id = u.id) as committee_ids, -(select array_agg(n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, +(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, +(select array_agg(n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, (select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, (select array_agg(o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, (select array_agg(v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, @@ -1286,20 +1286,20 @@ comment on column user_.committee_ids is 'Calculated field: Returns committee_id CREATE VIEW meeting_user AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion n where n.meeting_user_id = m.id) as supported_motion_ids, (select array_agg(me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, (select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, (select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, (select array_agg(a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, (select array_agg(mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, (select array_agg(c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, -(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids +(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_user n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level n where n.meeting_user_id = m.id) as structure_level_ids FROM meeting_user_t m; CREATE VIEW organization_tag AS SELECT *, -(select array_agg(g.id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids +(select array_agg(g.id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; @@ -1310,11 +1310,11 @@ FROM theme_t t; CREATE VIEW committee AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -(select array_agg(n.user_id) from nm_committee_user_ids_user_t n where n.committee_id = c.id) as user_ids, -(select array_agg(n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, -(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, -(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids +(select array_agg(n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, +(select array_agg(n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, +(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, +(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; @@ -1340,9 +1340,9 @@ CREATE VIEW meeting AS SELECT *, (select array_agg(t.id) from topic_t t where t.meeting_id = m.id) as topic_ids, (select array_agg(g.id) from group_t g where g.meeting_id = m.id) as group_ids, (select array_agg(mm.id) from meeting_mediafile_t mm where mm.meeting_id = m.id) as meeting_mediafile_ids, -(select array_agg(m1.id) from mediafile_t m1 where m1.owner_id_meeting_id = m.id) as mediafile_ids, -(select array_agg(m1.id) from motion_t m1 where m1.meeting_id = m.id) as motion_ids, -(select array_agg(m1.id) from motion_t m1 where m1.origin_meeting_id = m.id) as forwarded_motion_ids, +(select array_agg(mt.id) from mediafile_t mt where mt.owner_id_meeting_id = m.id) as mediafile_ids, +(select array_agg(mt.id) from motion_t mt where mt.meeting_id = m.id) as motion_ids, +(select array_agg(mt.id) from motion_t mt where mt.origin_meeting_id = m.id) as forwarded_motion_ids, (select array_agg(mc.id) from motion_comment_section_t mc where mc.meeting_id = m.id) as motion_comment_section_ids, (select array_agg(mc.id) from motion_category_t mc where mc.meeting_id = m.id) as motion_category_ids, (select array_agg(mb.id) from motion_block_t mb where mb.meeting_id = m.id) as motion_block_ids, @@ -1364,8 +1364,8 @@ CREATE VIEW meeting AS SELECT *, (select array_agg(c.id) from chat_message_t c where c.meeting_id = m.id) as chat_message_ids, (select array_agg(s.id) from structure_level_t s where s.meeting_id = m.id) as structure_level_ids, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, -(select array_agg(n.user_id) from nm_meeting_present_user_ids_user_t n where n.meeting_id = m.id) as present_user_ids, +(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, @@ -1385,35 +1385,35 @@ FROM meeting_t m; CREATE VIEW structure_level AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, (select array_agg(sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids FROM structure_level_t s; CREATE VIEW group_ AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, +(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, -(select array_agg(n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_access_group_ids, -(select array_agg(n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_group_t n where n.group_id = g.id) as write_chat_group_ids, -(select array_agg(n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids +(select array_agg(n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_access_group_ids, +(select array_agg(n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_group n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_group n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id) from nm_group_poll_ids_poll n where n.group_id = g.id) as poll_ids FROM group_t g; comment on column group_.meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; CREATE VIEW tag AS SELECT *, -(select array_agg(g.id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids +(select array_agg(g.id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids FROM tag_t t; CREATE VIEW agenda_item AS SELECT *, (select array_agg(ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_agenda_item_id = a.id) as tag_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_item_t a; @@ -1426,7 +1426,7 @@ FROM list_of_speakers_t l; CREATE VIEW structure_level_list_of_speakers AS SELECT *, -(select array_agg(s1.id) from speaker_t s1 where s1.structure_level_list_of_speakers_id = s.id) as speaker_ids +(select array_agg(st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids FROM structure_level_list_of_speakers_t s; @@ -1436,7 +1436,7 @@ FROM point_of_order_category_t p; CREATE VIEW topic AS SELECT *, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, (select array_agg(p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, @@ -1445,18 +1445,18 @@ FROM topic_t t; CREATE VIEW motion AS SELECT *, -(select array_agg(m1.id) from motion_t m1 where m1.lead_motion_id = m.id) as amendment_ids, -(select array_agg(m1.id) from motion_t m1 where m1.sort_parent_id = m.id) as sort_child_ids, -(select array_agg(m1.id) from motion_t m1 where m1.origin_id = m.id) as derived_motion_ids, -(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_derived_motion_id = m.id) as all_origin_ids, -(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_origin_id = m.id) as all_derived_motion_ids, -(select array_cat((select array_agg(n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, -(select array_agg(g.id) from gm_motion_state_extension_reference_ids_t g where g.motion_id = m.id) as state_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids_t g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, -(select array_agg(g.id) from gm_motion_recommendation_extension_reference_ids_t g where g.motion_id = m.id) as recommendation_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_ids_t g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(mt.id) from motion_t mt where mt.lead_motion_id = m.id) as amendment_ids, +(select array_agg(mt.id) from motion_t mt where mt.sort_parent_id = m.id) as sort_child_ids, +(select array_agg(mt.id) from motion_t mt where mt.origin_id = m.id) as derived_motion_ids, +(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion n where n.all_derived_motion_id = m.id) as all_origin_ids, +(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_cat((select array_agg(n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, +(select array_agg(g.id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_ids g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, -(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion n where n.motion_id = m.id) as supporter_meeting_user_ids, (select array_agg(me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, @@ -1465,8 +1465,8 @@ CREATE VIEW motion AS SELECT *, (select array_agg(mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, (select array_agg(p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids FROM motion_t m; @@ -1474,19 +1474,19 @@ FROM motion_t m; CREATE VIEW motion_comment_section AS SELECT *, (select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, -(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as read_group_ids, -(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as write_group_ids +(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, +(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as write_group_ids FROM motion_comment_section_t m; CREATE VIEW motion_category AS SELECT *, (select array_agg(mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, -(select array_agg(m1.id) from motion_t m1 where m1.category_id = m.id) as motion_ids +(select array_agg(mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids FROM motion_category_t m; CREATE VIEW motion_block AS SELECT *, -(select array_agg(m1.id) from motion_t m1 where m1.block_id = m.id) as motion_ids, +(select array_agg(mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, (select array_agg(p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids @@ -1495,10 +1495,10 @@ FROM motion_block_t m; CREATE VIEW motion_state AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, -(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.previous_state_id = m.id) as next_state_ids, -(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.next_state_id = m.id) as previous_state_ids, -(select array_agg(m1.id) from motion_t m1 where m1.state_id = m.id) as motion_ids, -(select array_agg(m1.id) from motion_t m1 where m1.recommendation_id = m.id) as motion_recommendation_ids, +(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, +(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state n where n.next_state_id = m.id) as previous_state_ids, +(select array_agg(mt.id) from motion_t mt where mt.state_id = m.id) as motion_ids, +(select array_agg(mt.id) from motion_t mt where mt.recommendation_id = m.id) as motion_recommendation_ids, (select mw.id from motion_workflow_t mw where mw.first_state_id = m.id) as first_state_of_workflow_id FROM motion_state_t m; @@ -1512,15 +1512,15 @@ FROM motion_workflow_t m; CREATE VIEW motion_statute_paragraph AS SELECT *, -(select array_agg(m1.id) from motion_t m1 where m1.statute_paragraph_id = m.id) as motion_ids +(select array_agg(mt.id) from motion_t mt where mt.statute_paragraph_id = m.id) as motion_ids FROM motion_statute_paragraph_t m; CREATE VIEW poll AS SELECT *, (select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, -(select array_agg(n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, -(select array_agg(n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, -(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_poll_id = p.id) as projection_ids +(select array_agg(n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; @@ -1535,8 +1535,8 @@ CREATE VIEW assignment AS SELECT *, (select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids FROM assignment_t a; @@ -1548,17 +1548,17 @@ FROM poll_candidate_list_t p; CREATE VIEW mediafile AS SELECT *, -(select array_agg(m1.id) from mediafile_t m1 where m1.parent_id = m.id) as child_ids, +(select array_agg(mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, (select array_agg(mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids FROM mediafile_t m; CREATE VIEW meeting_mediafile AS SELECT *, -(select array_agg(n.group_id) from nm_group_mmiagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as access_group_ids, +(select array_agg(n.group_id) from nm_group_mmiagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, (select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, (select array_agg(p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_meeting_mediafile_attachment_ids_t g where g.meeting_mediafile_id = m.id) as attachment_ids, +(select array_agg(g.id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, (select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, @@ -1580,20 +1580,20 @@ FROM meeting_mediafile_t m; comment on column meeting_mediafile.inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; CREATE VIEW projector AS SELECT *, -(select array_agg(p1.id) from projection_t p1 where p1.current_projector_id = p.id) as current_projection_ids, -(select array_agg(p1.id) from projection_t p1 where p1.preview_projector_id = p.id) as preview_projection_ids, -(select array_agg(p1.id) from projection_t p1 where p1.history_projector_id = p.id) as history_projection_ids, +(select array_agg(pt.id) from projection_t pt where pt.current_projector_id = p.id) as current_projection_ids, +(select array_agg(pt.id) from projection_t pt where pt.preview_projector_id = p.id) as preview_projection_ids, +(select array_agg(pt.id) from projection_t pt where pt.history_projector_id = p.id) as history_projection_ids, (select m.id from meeting_t m where m.reference_projector_id = p.id) as used_as_reference_projector_meeting_id FROM projector_t p; CREATE VIEW projector_message AS SELECT *, -(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_message_id = p.id) as projection_ids +(select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_message_t p; CREATE VIEW projector_countdown AS SELECT *, -(select array_agg(p1.id) from projection_t p1 where p1.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, (select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, (select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdown_t p; @@ -1601,8 +1601,8 @@ FROM projector_countdown_t p; CREATE VIEW chat_group AS SELECT *, (select array_agg(cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, -(select array_agg(n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids +(select array_agg(n.group_id) from nm_chat_group_read_group_ids_group n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids FROM chat_group_t c; -- Alter table relations diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index b52b25b3..878780c9 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -365,7 +365,9 @@ def get_relation_list_type( foreign_table_column += ( f"_{table_name}_{foreign_table_field.ref_column}" ) - foreign_table_name = foreign_table_field.table + foreign_table_name = HelperGetNames.get_table_name( + foreign_table_field.table + ) foreign_table_ref_column = foreign_table_field.ref_column elif type_ == "relation-list": if own_table_field.table == foreign_table_field.table: @@ -397,7 +399,9 @@ def get_relation_list_type( elif type_ == "relation" or foreign_table_field_ref_id: own_ref_column = own_table_field.ref_column foreign_table_ref_column = foreign_table_field.ref_column - foreign_table_name = foreign_table_field.table + foreign_table_name = HelperGetNames.get_table_name( + foreign_table_field.table + ) foreign_table_column = foreign_table_field.column else: raise Exception( @@ -441,7 +445,6 @@ def get_sql_for_relation_n_1( ) -> str: table_letter = Helper.get_table_letter(table_name) foreign_letter = Helper.get_table_letter(foreign_table_name, [table_letter]) - foreign_table_name = HelperGetNames.get_table_name(foreign_table_name) AGG_TEMPLATE = f"select array_agg({foreign_letter}.{{}}) from {foreign_table_name} {foreign_letter}" COND_TEMPLATE = ( f" where {foreign_letter}.{{}} = {table_letter}.{own_ref_column}" @@ -807,7 +810,7 @@ def get_nm_table_for_n_m_relation_lists( field2 += "_2" text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute( { - "table_name": HelperGetNames.get_table_name(nm_table_name), + "table_name": nm_table_name, # without tailing _t "field1": field1, "table1": HelperGetNames.get_table_name(own_table_field.table), "field2": field2, @@ -850,7 +853,7 @@ def get_gm_table_for_gm_nm_relation_lists( text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute( { - "table_name": HelperGetNames.get_table_name(gm_table_name), + "table_name": gm_table_name, # without trailing _t "own_table_name": HelperGetNames.get_table_name(own_table_field.table), "own_table_name_with_ref_column": ( own_table_name_with_ref_column := f"{own_table_field.table}_{own_table_field.ref_column}" From 6e3fce02df2633f087c9cfa6edf4fc733dcf6b61 Mon Sep 17 00:00:00 2001 From: peb-adr Date: Thu, 6 Mar 2025 16:43:10 +0100 Subject: [PATCH 075/142] NOTIFY trigger (#212) * NOTIFY trigger v1 including: - use of three channels 'insert', 'update', 'delete' - payload in form of 'collection/changed_model_id' - os_notify_log_t table recording notifications * Satisfy tests and formatters --- dev/sql/schema_relational.sql | 138 ++++++++++++++++++++++++++++++++- dev/src/generate_sql_schema.py | 92 ++++++++++++++++++---- 2 files changed, 211 insertions(+), 19 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index a19ca40e..18dc8801 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,6 +1,11 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. +-- MODELS_YML_CHECKSUM = 'ff3a9aa7702358f023a5184aec6536d5' + + +-- Function and meta table definitions + CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ @@ -42,10 +47,38 @@ begin RETURN NULL; -- AFTER TRIGGER needs no return end; $not_null_trigger$ language plpgsql; --- MODELS_YML_CHECKSUM = 'ff3a9aa7702358f023a5184aec6536d5' + +CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ +DECLARE + channel TEXT; + payload TEXT; +BEGIN + channel:= LOWER(TG_OP); + payload := TG_TABLE_NAME || '/' || NEW.id; + IF (TG_OP = 'DELETE') THEN + payload = TG_TABLE_NAME || '/' || OLD.id; + END IF; + + PERFORM pg_notify(channel, payload); + INSERT INTO os_notify_log_t (channel, payload, xact_id, timestamp) VALUES (channel, payload, pg_current_xact_id(), 'now'); + RETURN NULL; -- AFTER TRIGGER needs no return +END; +$notify_trigger$ LANGUAGE plpgsql; + +CREATE TABLE os_notify_log_t ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + channel varchar(32), + payload varchar(256), + xact_id xid8, + timestamp timestamptz +); + + -- Type definitions + -- Table definitions + CREATE TABLE organization_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name varchar(256), @@ -1253,6 +1286,8 @@ CREATE TABLE nm_chat_group_write_group_ids_group ( group_id integer NOT NULL REFERENCES group_t (id), PRIMARY KEY (chat_group_id, group_id) ); + + -- View definitions CREATE VIEW organization AS SELECT *, @@ -1605,6 +1640,8 @@ CREATE VIEW chat_group AS SELECT *, (select array_agg(n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids FROM chat_group_t c; + + -- Alter table relations ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; @@ -1812,7 +1849,9 @@ ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_u ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; --- Create trigger + + +-- Create triggers checking foreign_id not null for relation-lists -- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON projector_t INITIALLY DEFERRED @@ -1927,6 +1966,101 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa + +-- Create triggers for notify +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_statute_paragraph_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + /* Relation-list infos Generated: What will be generated for left field FIELD: a usual Database field diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 878780c9..5c62706d 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -30,7 +30,8 @@ class SchemaZoneTexts(TypedDict, total=False): post_view: str alter_table: str alter_table_final: str - create_trigger: str + create_trigger_relationlistnotnull: str + create_trigger_notify: str undecided: str final_info: str errors: list[str] @@ -68,7 +69,7 @@ class GenerateCodeBlocks: @classmethod def generate_the_code( cls, - ) -> tuple[str, str, str, str, str, list[str], str, str, list[str]]: + ) -> tuple[str, str, str, str, str, list[str], str, str, str, list[str]]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -80,7 +81,8 @@ def generate_the_code( im_table_code: Code for intermediate tables. n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field - create_trigger_code Definitions of triggers + create_trigger_relationlistnotnull_code: Definitions of triggers calling check_not_null_for_relation_lists + create_trigger_notify_code: Definitions of triggers calling notify_modified_models errors: to show """ handled_attributes = { @@ -107,7 +109,8 @@ def generate_the_code( table_name_code: str = "" view_name_code: str = "" alter_table_final_code: str = "" - create_trigger_code: str = "" + create_trigger_relationlistnotnull_code: str = "" + create_trigger_notify_code: str = "" final_info_code: str = "" missing_handled_attributes = [] im_table_code = "" @@ -152,13 +155,24 @@ def generate_the_code( view_name_code += code if code := schema_zone_texts["alter_table_final"]: alter_table_final_code += code + "\n" - if code := schema_zone_texts["create_trigger"]: - create_trigger_code += code + "\n" + if code := schema_zone_texts["create_trigger_relationlistnotnull"]: + create_trigger_relationlistnotnull_code += code + "\n" if code := schema_zone_texts["final_info"]: final_info_code += code + "\n" for im_table in cls.intermediate_tables.values(): im_table_code += im_table + # schema_zone_texts is filled per model field. + # If any fields for this collection generated table code, create the main notify trigger on it. + if schema_zone_texts["table"]: + create_trigger_notify_code += ( + Helper.get_notify_trigger(table_name) + "\n" + ) + # Special triggers (e.g. for relation fields) come after + # TODO: needs to be filled in the get_*_relation_*_type functions + if code := schema_zone_texts["create_trigger_notify"]: + create_trigger_notify_code += code + "\n" + return ( pre_code, table_name_code, @@ -167,7 +181,8 @@ def generate_the_code( final_info_code, missing_handled_attributes, im_table_code, - create_trigger_code, + create_trigger_relationlistnotnull_code, + create_trigger_notify_code, errors, ) @@ -421,7 +436,7 @@ def get_relation_list_type( HelperGetNames.get_view_name(table_name), fname, comment ) if own_table_field.field_def.get("required"): - text["create_trigger"] = ( + text["create_trigger_relationlistnotnull"] = ( cls.get_trigger_check_not_null_for_relation_lists( own_table_field.table, own_table_field.column, @@ -574,10 +589,14 @@ def get_generic_relation_list_type( class Helper: - FILE_TEMPLATE = dedent( + FILE_TEMPLATE_HEADER = dedent( """ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. + """ + ) + FILE_TEMPLATE_CONSTANT_DEFINITIONS = dedent( + """ CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ @@ -619,6 +638,31 @@ class Helper: RETURN NULL; -- AFTER TRIGGER needs no return end; $not_null_trigger$ language plpgsql; + + CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ + DECLARE + channel TEXT; + payload TEXT; + BEGIN + channel:= LOWER(TG_OP); + payload := TG_TABLE_NAME || '/' || NEW.id; + IF (TG_OP = 'DELETE') THEN + payload = TG_TABLE_NAME || '/' || OLD.id; + END IF; + + PERFORM pg_notify(channel, payload); + INSERT INTO os_notify_log_t (channel, payload, xact_id, timestamp) VALUES (channel, payload, pg_current_xact_id(), 'now'); + RETURN NULL; -- AFTER TRIGGER needs no return + END; + $notify_trigger$ LANGUAGE plpgsql; + + CREATE TABLE os_notify_log_t ( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + channel varchar(32), + payload varchar(256), + xact_id xid8, + timestamp timestamptz + ); """ ) FIELD_TEMPLATE = string.Template( @@ -716,6 +760,13 @@ def get_view_body_end(table_name: str, code: str) -> str: code += f"FROM {HelperGetNames.get_table_name(table_name)} {Helper.get_table_letter(table_name)};\n\n" return code + @staticmethod + def get_notify_trigger(table_name: str) -> str: + own_table = HelperGetNames.get_table_name(table_name) + code = f"CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" + code += "FOR EACH ROW EXECUTE FUNCTION notify_modified_models();" + return code + @staticmethod def get_alter_table_final_code(code: str) -> str: return f"-- Alter table final relation commands\n{code}\n\n" @@ -1078,24 +1129,31 @@ def main() -> None: final_info_code, missing_handled_attributes, im_table_code, - create_trigger_code, + create_trigger_relationlistnotnull_code, + create_trigger_notify_code, errors, ) = GenerateCodeBlocks.generate_the_code() with open(DESTINATION, "w") as dest: - dest.write(Helper.FILE_TEMPLATE) + dest.write(Helper.FILE_TEMPLATE_HEADER) dest.write("-- MODELS_YML_CHECKSUM = " + repr(checksum) + "\n") - dest.write("-- Type definitions") + dest.write("\n\n-- Function and meta table definitions\n") + dest.write(Helper.FILE_TEMPLATE_CONSTANT_DEFINITIONS) + dest.write("\n\n-- Type definitions\n") dest.write(pre_code) - dest.write("\n\n-- Table definitions") + dest.write("\n\n-- Table definitions\n") dest.write(table_name_code) dest.write("\n\n-- Intermediate table definitions\n") dest.write(im_table_code) - dest.write("-- View definitions\n") + dest.write("\n\n-- View definitions\n") dest.write(view_name_code) - dest.write("-- Alter table relations\n") + dest.write("\n\n-- Alter table relations\n") dest.write(alter_table_code) - dest.write("-- Create trigger\n") - dest.write(create_trigger_code) + dest.write( + "\n\n-- Create triggers checking foreign_id not null for relation-lists\n" + ) + dest.write(create_trigger_relationlistnotnull_code) + dest.write("\n\n-- Create triggers for notify\n") + dest.write(create_trigger_notify_code) dest.write(Helper.RELATION_LIST_AGENDA) dest.write("/*\n") dest.write(final_info_code) From 760ffa4ccb72c295ec4042930ef5071610003e3b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Tue, 1 Apr 2025 10:34:26 +0200 Subject: [PATCH 076/142] Generate schema after merge --- dev/requirements.txt | 2 +- dev/sql/schema_relational.sql | 177 ++++++++++++++++------------------ 2 files changed, 85 insertions(+), 94 deletions(-) diff --git a/dev/requirements.txt b/dev/requirements.txt index 5063257e..48b730a3 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -9,7 +9,7 @@ pyyaml==6.0.2 simplejson==3.20.1 debugpy==1.8.0 requests==2.31.0 -psycopg[binary]==3.1.18 +psycopg[binary]==3.2.6 python-sql==1.5.1 # typing diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 18dc8801..5272e0c7 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'ff3a9aa7702358f023a5184aec6536d5' +-- MODELS_YML_CHECKSUM = 'b032c8ffd6e7dab1ae122c190520ce74' -- Function and meta table definitions @@ -80,20 +80,20 @@ CREATE TABLE os_notify_log_t ( -- Table definitions CREATE TABLE organization_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256), description text, legal_notice text, privacy_policy text, login_text text, reset_password_verbose_errors boolean, - genders varchar(256)[] DEFAULT '{"male", "female", "diverse", "non-binary"}', enable_electronic_voting boolean, enable_chat boolean, limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, default_language varchar(256) NOT NULL CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), require_duplicate_from boolean, + enable_anonymous boolean, saml_enabled boolean, saml_login_button_text varchar(256) DEFAULT 'SAML login', saml_attr_mapping jsonb, @@ -130,7 +130,7 @@ comment on column organization_t.limit_of_users is 'Maximum of active users for */ CREATE TABLE user_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, username varchar(256) NOT NULL, member_number varchar(256), saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), @@ -143,12 +143,12 @@ CREATE TABLE user_t ( password varchar(256), default_password varchar(256), can_change_own_password boolean DEFAULT True, - gender varchar(256), email varchar(256), default_vote_weight decimal(6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', last_email_sent timestamptz, is_demo_user boolean, last_login timestamptz, + gender_id integer, organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL @@ -176,8 +176,19 @@ CREATE TABLE meeting_user_t ( +CREATE TABLE gender_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name varchar(256) NOT NULL, + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL +); + + + +comment on column gender_t.name is 'unique'; + + CREATE TABLE organization_tag_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') NOT NULL, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL @@ -242,12 +253,11 @@ CREATE TABLE theme_t ( CREATE TABLE committee_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, description text, external_id varchar(256), default_meeting_id integer, - forwarding_user_id integer, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -257,7 +267,7 @@ comment on column committee_t.external_id is 'unique'; CREATE TABLE meeting_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, external_id varchar(256), welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', welcome_text text DEFAULT 'Space for your welcome text.', @@ -334,26 +344,27 @@ CREATE TABLE meeting_t ( list_of_speakers_intervention_time integer, motions_default_workflow_id integer NOT NULL, motions_default_amendment_workflow_id integer NOT NULL, - motions_default_statute_amendment_workflow_id integer NOT NULL, motions_preamble text DEFAULT 'The assembly may decide:', motions_default_line_numbering varchar(256) CONSTRAINT enum_meeting_motions_default_line_numbering CHECK (motions_default_line_numbering IN ('outside', 'inline', 'none')) DEFAULT 'outside', motions_line_length integer CONSTRAINT minimum_motions_line_length CHECK (motions_line_length >= 40) DEFAULT 85, motions_reason_required boolean DEFAULT False, + motions_origin_motion_toggle_default boolean DEFAULT False, + motions_enable_origin_motion_display boolean DEFAULT False, motions_enable_text_on_projector boolean DEFAULT True, motions_enable_reason_on_projector boolean DEFAULT False, motions_enable_sidebox_on_projector boolean DEFAULT False, motions_enable_recommendation_on_projector boolean DEFAULT True, + motions_hide_metadata_background boolean DEFAULT False, motions_show_referring_motions boolean DEFAULT True, motions_show_sequential_number boolean DEFAULT True, + motions_create_enable_additional_submitter_text boolean, motions_recommendations_by varchar(256), motions_block_slide_columns integer CONSTRAINT minimum_motions_block_slide_columns CHECK (motions_block_slide_columns >= 1), - motions_statute_recommendations_by varchar(256), motions_recommendation_text_mode varchar(256) CONSTRAINT enum_meeting_motions_recommendation_text_mode CHECK (motions_recommendation_text_mode IN ('original', 'changed', 'diff', 'agreed')) DEFAULT 'diff', motions_default_sorting varchar(256) CONSTRAINT enum_meeting_motions_default_sorting CHECK (motions_default_sorting IN ('number', 'weight')) DEFAULT 'number', motions_number_type varchar(256) CONSTRAINT enum_meeting_motions_number_type CHECK (motions_number_type IN ('per_category', 'serially_numbered', 'manually')) DEFAULT 'per_category', motions_number_min_digits integer DEFAULT 2, motions_number_with_blank boolean DEFAULT False, - motions_statutes_enabled boolean DEFAULT False, motions_amendments_enabled boolean DEFAULT True, motions_amendments_in_main_list boolean DEFAULT True, motions_amendments_of_amendments boolean DEFAULT False, @@ -373,6 +384,8 @@ CREATE TABLE meeting_t ( motion_poll_default_method varchar(256) DEFAULT 'YNA', motion_poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_motion_poll_default_onehundred_percent_base CHECK (motion_poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', motion_poll_default_backend varchar(256) CONSTRAINT enum_meeting_motion_poll_default_backend CHECK (motion_poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', + motion_poll_projection_name_order_first varchar(256) NOT NULL CONSTRAINT enum_meeting_motion_poll_projection_name_order_first CHECK (motion_poll_projection_name_order_first IN ('first_name', 'last_name')) DEFAULT 'last_name', + motion_poll_projection_max_columns integer NOT NULL DEFAULT 6, users_enable_presence_view boolean DEFAULT False, users_enable_vote_weight boolean DEFAULT False, users_allow_self_set_present boolean DEFAULT True, @@ -466,10 +479,10 @@ CREATE TABLE structure_level_t ( CREATE TABLE group_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, external_id varchar(256), name varchar(256) NOT NULL, - permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'agenda_item.can_manage_moderator_notes', 'agenda_item.can_see_moderator_notes', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_see_origin', 'motion.can_support', 'poll.can_manage', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update']::varchar[]), + permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'assignment.can_manage', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'list_of_speakers.can_manage_moderator_notes', 'list_of_speakers.can_see_moderator_notes', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_see_origin', 'motion.can_support', 'poll.can_manage', 'poll.can_see_progress', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update', 'user.can_edit_own_delegation']::varchar[]), weight integer, used_as_motion_poll_default_id integer, used_as_assignment_poll_default_id integer, @@ -484,7 +497,7 @@ comment on column group_t.external_id is 'unique in meeting'; CREATE TABLE personal_note_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, note text, star boolean, meeting_user_id integer NOT NULL, @@ -498,7 +511,7 @@ CREATE TABLE personal_note_t ( CREATE TABLE tag_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, meeting_id integer NOT NULL ); @@ -507,13 +520,12 @@ CREATE TABLE tag_t ( CREATE TABLE agenda_item_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, item_number varchar(256), comment varchar(256), closed boolean DEFAULT False, type varchar(256) CONSTRAINT enum_agenda_item_type CHECK (type IN ('common', 'internal', 'hidden')) DEFAULT 'common', duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), - moderator_notes text, is_internal boolean, is_hidden boolean, level integer, @@ -537,9 +549,10 @@ comment on column agenda_item_t.level is 'Calculated by the server'; CREATE TABLE list_of_speakers_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, closed boolean DEFAULT False, sequential_number integer NOT NULL, + moderator_notes text, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -575,7 +588,7 @@ comment on column structure_level_list_of_speakers_t.current_start_time is 'The CREATE TABLE point_of_order_category_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, text varchar(256) NOT NULL, rank integer NOT NULL, meeting_id integer NOT NULL @@ -585,7 +598,7 @@ CREATE TABLE point_of_order_category_t ( CREATE TABLE speaker_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, begin_time timestamptz, end_time timestamptz, pause_time timestamptz, @@ -606,7 +619,7 @@ CREATE TABLE speaker_t ( CREATE TABLE topic_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, text text, sequential_number integer NOT NULL, @@ -619,7 +632,7 @@ comment on column topic_t.sequential_number is 'The (positive) serial number of CREATE TABLE motion_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, number varchar(256), number_value integer, sequential_number integer NOT NULL, @@ -647,7 +660,6 @@ CREATE TABLE motion_t ( recommendation_id integer, category_id integer, block_id integer, - statute_paragraph_id integer, meeting_id integer NOT NULL ); @@ -658,7 +670,7 @@ comment on column motion_t.sequential_number is 'The (positive) serial number of CREATE TABLE motion_submitter_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, meeting_user_id integer NOT NULL, motion_id integer NOT NULL, @@ -669,7 +681,7 @@ CREATE TABLE motion_submitter_t ( CREATE TABLE motion_editor_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, meeting_user_id integer NOT NULL, motion_id integer NOT NULL, @@ -680,7 +692,7 @@ CREATE TABLE motion_editor_t ( CREATE TABLE motion_working_group_speaker_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, meeting_user_id integer NOT NULL, motion_id integer NOT NULL, @@ -691,7 +703,7 @@ CREATE TABLE motion_working_group_speaker_t ( CREATE TABLE motion_comment_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, comment text, motion_id integer NOT NULL, section_id integer NOT NULL, @@ -702,7 +714,7 @@ CREATE TABLE motion_comment_t ( CREATE TABLE motion_comment_section_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, weight integer DEFAULT 10000, sequential_number integer NOT NULL, @@ -716,7 +728,7 @@ comment on column motion_comment_section_t.sequential_number is 'The (positive) CREATE TABLE motion_category_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, prefix varchar(256), weight integer DEFAULT 10000, @@ -733,7 +745,7 @@ comment on column motion_category_t.sequential_number is 'The (positive) serial CREATE TABLE motion_block_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, internal boolean, sequential_number integer NOT NULL, @@ -746,7 +758,7 @@ comment on column motion_block_t.sequential_number is 'The (positive) serial num CREATE TABLE motion_change_recommendation_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, rejected boolean DEFAULT False, internal boolean DEFAULT False, type varchar(256) CONSTRAINT enum_motion_change_recommendation_type CHECK (type IN ('replacement', 'insertion', 'deletion', 'other')) DEFAULT 'replacement', @@ -763,7 +775,7 @@ CREATE TABLE motion_change_recommendation_t ( CREATE TABLE motion_state_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, weight integer NOT NULL, recommendation_label varchar(256), @@ -788,7 +800,7 @@ CREATE TABLE motion_state_t ( CREATE TABLE motion_workflow_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, sequential_number integer NOT NULL, first_state_id integer NOT NULL, @@ -800,22 +812,8 @@ CREATE TABLE motion_workflow_t ( comment on column motion_workflow_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE motion_statute_paragraph_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, - title varchar(256) NOT NULL, - text text, - weight integer DEFAULT 10000, - sequential_number integer NOT NULL, - meeting_id integer NOT NULL -); - - - -comment on column motion_statute_paragraph_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - - CREATE TABLE poll_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, description text, title varchar(256) NOT NULL, type varchar(256) NOT NULL CONSTRAINT enum_poll_type CHECK (type IN ('analog', 'named', 'pseudoanonymous', 'cryptographic')), @@ -859,12 +857,12 @@ comment on column poll_t.votes_signature is 'base64 signature of votes_raw field /* Fields without SQL definition for table poll - poll/vote_count: type:number is marked as a calculated field and not generated in schema + poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema */ CREATE TABLE option_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer DEFAULT 10000, text text, yes decimal(6), @@ -883,7 +881,7 @@ CREATE TABLE option_t ( CREATE TABLE vote_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight decimal(6), value varchar(256), user_token varchar(256) NOT NULL, @@ -897,7 +895,7 @@ CREATE TABLE vote_t ( CREATE TABLE assignment_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, description text, open_posts integer CONSTRAINT minimum_open_posts CHECK (open_posts >= 0) DEFAULT 0, @@ -914,7 +912,7 @@ comment on column assignment_t.sequential_number is 'The (positive) serial numbe CREATE TABLE assignment_candidate_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer DEFAULT 10000, assignment_id integer NOT NULL, meeting_user_id integer, @@ -925,7 +923,7 @@ CREATE TABLE assignment_candidate_t ( CREATE TABLE poll_candidate_list_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, meeting_id integer NOT NULL ); @@ -933,7 +931,7 @@ CREATE TABLE poll_candidate_list_t ( CREATE TABLE poll_candidate_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, poll_candidate_list_id integer NOT NULL, user_id integer, weight integer NOT NULL, @@ -944,7 +942,7 @@ CREATE TABLE poll_candidate_t ( CREATE TABLE mediafile_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256), is_directory boolean, filesize integer, @@ -969,7 +967,7 @@ comment on column mediafile_t.filename is 'The uploaded filename. Will be used f CREATE TABLE meeting_mediafile_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, mediafile_id integer NOT NULL, meeting_id integer NOT NULL, is_public boolean NOT NULL @@ -981,7 +979,7 @@ comment on column meeting_mediafile_t.is_public is 'Calculated in actions. Used CREATE TABLE projector_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256), is_internal boolean DEFAULT False, scale integer DEFAULT 0, @@ -1026,7 +1024,7 @@ comment on column projector_t.sequential_number is 'The (positive) serial number CREATE TABLE projection_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, options jsonb, stable boolean DEFAULT False, weight integer, @@ -1060,7 +1058,7 @@ CREATE TABLE projection_t ( */ CREATE TABLE projector_message_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, message text, meeting_id integer NOT NULL ); @@ -1069,7 +1067,7 @@ CREATE TABLE projector_message_t ( CREATE TABLE projector_countdown_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, description varchar(256) DEFAULT '', default_time integer, @@ -1082,7 +1080,7 @@ CREATE TABLE projector_countdown_t ( CREATE TABLE chat_group_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, weight integer DEFAULT 10000, meeting_id integer NOT NULL @@ -1092,7 +1090,7 @@ CREATE TABLE chat_group_t ( CREATE TABLE chat_message_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, content text NOT NULL, created timestamptz NOT NULL, meeting_user_id integer, @@ -1104,19 +1102,22 @@ CREATE TABLE chat_message_t ( CREATE TABLE action_worker_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, state varchar(256) NOT NULL CONSTRAINT enum_action_worker_state CHECK (state IN ('running', 'end', 'aborted')), created timestamptz NOT NULL, timestamp timestamptz NOT NULL, - result jsonb + result jsonb, + user_id integer NOT NULL ); +comment on column action_worker_t.user_id is 'Id of the calling user. If the action is called via internal route, the value will be -1.'; + CREATE TABLE import_preview_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL CONSTRAINT enum_import_preview_name CHECK (name IN ('account', 'participant', 'topic', 'committee', 'motion')), state varchar(256) NOT NULL CONSTRAINT enum_import_preview_state CHECK (state IN ('warning', 'error', 'done')), created timestamptz NOT NULL, @@ -1291,6 +1292,7 @@ CREATE TABLE nm_chat_group_write_group_ids_group ( -- View definitions CREATE VIEW organization AS SELECT *, +(select array_agg(g.id) from gender_t g where g.organization_id = o.id) as gender_ids, (select array_agg(c.id) from committee_t c where c.organization_id = o.id) as committee_ids, (select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, (select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, @@ -1307,7 +1309,6 @@ CREATE VIEW user_ AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, (select array_agg(n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, -(select array_agg(c.id) from committee_t c where c.forwarding_user_id = u.id) as forwarding_committee_ids, (select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, (select array_agg(n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, (select array_agg(o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, @@ -1333,6 +1334,11 @@ CREATE VIEW meeting_user AS SELECT *, FROM meeting_user_t m; +CREATE VIEW gender AS SELECT *, +(select array_agg(u.id) from user_t u where u.gender_id = g.id) as user_ids +FROM gender_t g; + + CREATE VIEW organization_tag AS SELECT *, (select array_agg(g.id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; @@ -1382,7 +1388,6 @@ CREATE VIEW meeting AS SELECT *, (select array_agg(mc.id) from motion_category_t mc where mc.meeting_id = m.id) as motion_category_ids, (select array_agg(mb.id) from motion_block_t mb where mb.meeting_id = m.id) as motion_block_ids, (select array_agg(mw.id) from motion_workflow_t mw where mw.meeting_id = m.id) as motion_workflow_ids, -(select array_agg(ms.id) from motion_statute_paragraph_t ms where ms.meeting_id = m.id) as motion_statute_paragraph_ids, (select array_agg(mc.id) from motion_comment_t mc where mc.meeting_id = m.id) as motion_comment_ids, (select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_id = m.id) as motion_submitter_ids, (select array_agg(me.id) from motion_editor_t me where me.meeting_id = m.id) as motion_editor_ids, @@ -1541,16 +1546,10 @@ FROM motion_state_t m; CREATE VIEW motion_workflow AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, (select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, -(select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id, -(select m1.id from meeting_t m1 where m1.motions_default_statute_amendment_workflow_id = m.id) as default_statute_amendment_workflow_meeting_id +(select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id FROM motion_workflow_t m; -CREATE VIEW motion_statute_paragraph AS SELECT *, -(select array_agg(mt.id) from motion_t mt where mt.statute_paragraph_id = m.id) as motion_ids -FROM motion_statute_paragraph_t m; - - CREATE VIEW poll AS SELECT *, (select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, (select array_agg(n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, @@ -1645,19 +1644,19 @@ FROM chat_group_t c; -- Alter table relations ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; +ALTER TABLE user_t ADD FOREIGN KEY(gender_id) REFERENCES gender_t(id) INITIALLY DEFERRED; + ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE committee_t ADD FOREIGN KEY(forwarding_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_statute_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; @@ -1732,7 +1731,6 @@ ALTER TABLE motion_t ADD FOREIGN KEY(state_id) REFERENCES motion_state_t(id) INI ALTER TABLE motion_t ADD FOREIGN KEY(recommendation_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_t ADD FOREIGN KEY(statute_paragraph_id) REFERENCES motion_statute_paragraph_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; @@ -1768,8 +1766,6 @@ ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_statute_paragraph_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; @@ -1974,6 +1970,8 @@ CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t +FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t @@ -2024,8 +2022,6 @@ CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_statute_paragraph_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t @@ -2080,6 +2076,7 @@ Model.Field -> Model.Field */ /* +SQL nr:1rR => organization/gender_ids:-> gender/organization_id SQL nr:1rR => organization/committee_ids:-> committee/organization_id SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id @@ -2091,10 +2088,10 @@ SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id SQL nt:1r => organization/published_mediafile_ids:-> mediafile/published_to_meetings_in_organization_id SQL nr:1rR => organization/user_ids:-> user/organization_id +FIELD 1r:nr => user/gender_id:-> gender/user_ids SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nt:nt => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids -SQL nt:1r => user/forwarding_committee_ids:-> committee/forwarding_user_id SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids SQL nt:1Gr => user/option_ids:-> option/content_object_id @@ -2117,6 +2114,8 @@ SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids +SQL nr:1r => gender/user_ids:-> user/gender_id + SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id @@ -2127,7 +2126,6 @@ SQL nt:nt => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids -FIELD 1r:nt => committee/forwarding_user_id:-> user/forwarding_committee_ids SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids FIELD 1r:nt => meeting/is_active_in_organization_id:-> organization/active_meeting_ids @@ -2135,7 +2133,6 @@ FIELD 1r:nt => meeting/is_archived_in_organization_id:-> organization/archived_m FIELD 1r:nt => meeting/template_for_organization_id:-> organization/template_meeting_ids FIELD 1rR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id FIELD 1rR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id -FIELD 1rR:1t => meeting/motions_default_statute_amendment_workflow_id:-> motion_workflow/default_statute_amendment_workflow_meeting_id SQL nt:1r => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id SQL nt:1rR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id SQL nt:1rR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id @@ -2163,7 +2160,6 @@ SQL nt:1rR => meeting/motion_comment_section_ids:-> motion_comment_section/meeti SQL nt:1rR => meeting/motion_category_ids:-> motion_category/meeting_id SQL nt:1rR => meeting/motion_block_ids:-> motion_block/meeting_id SQL nt:1rR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id -SQL nt:1rR => meeting/motion_statute_paragraph_ids:-> motion_statute_paragraph/meeting_id SQL nt:1rR => meeting/motion_comment_ids:-> motion_comment/meeting_id SQL nt:1rR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id SQL nt:1rR => meeting/motion_editor_ids:-> motion_editor/meeting_id @@ -2308,7 +2304,6 @@ SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/m SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id SQL nt:1Gr => motion/option_ids:-> option/content_object_id SQL nt:1rR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id -FIELD 1r:nt => motion/statute_paragraph_id:-> motion_statute_paragraph/motion_ids SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id SQL 1tR:1GrR => motion/list_of_speakers_id:-> list_of_speakers/content_object_id @@ -2367,12 +2362,8 @@ SQL nt:1rR => motion_workflow/state_ids:-> motion_state/workflow_id FIELD 1rR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id -SQL 1t:1rR => motion_workflow/default_statute_amendment_workflow_meeting_id:-> meeting/motions_default_statute_amendment_workflow_id FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids -SQL nt:1r => motion_statute_paragraph/motion_ids:-> motion/statute_paragraph_id -FIELD 1rR:nt => motion_statute_paragraph/meeting_id:-> meeting/motion_statute_paragraph_ids - FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ SQL nt:1r => poll/option_ids:-> option/poll_id FIELD 1r:1t => poll/global_option_id:-> option/used_as_global_option_in_poll_id @@ -2490,7 +2481,7 @@ FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids /* There are 3 errors/warnings organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema - poll/vote_count: type:number is marked as a calculated field and not generated in schema + poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema projection/content: type:JSON is marked as a calculated field and not generated in schema */ From c45d201e2c2a1b4ac997ccf4fd9fb3b506c3e43a Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 4 Apr 2025 11:00:15 +0200 Subject: [PATCH 077/142] Fix decimal type (#248) --- dev/sql/schema_relational.sql | 18 +++++++++--------- dev/src/generate_sql_schema.py | 6 +----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 5272e0c7..df81820b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -144,7 +144,7 @@ CREATE TABLE user_t ( default_password varchar(256), can_change_own_password boolean DEFAULT True, email varchar(256), - default_vote_weight decimal(6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', + default_vote_weight decimal(16,6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', last_email_sent timestamptz, is_demo_user boolean, last_login timestamptz, @@ -166,7 +166,7 @@ CREATE TABLE meeting_user_t ( comment text, number varchar(256), about_me text, - vote_weight decimal(6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), + vote_weight decimal(16,6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), locked_out boolean, user_id integer NOT NULL, meeting_id integer NOT NULL, @@ -828,9 +828,9 @@ CREATE TABLE poll_t ( global_no boolean DEFAULT False, global_abstain boolean DEFAULT False, onehundred_percent_base varchar(256) NOT NULL CONSTRAINT enum_poll_onehundred_percent_base CHECK (onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'disabled', - votesvalid decimal(6), - votesinvalid decimal(6), - votescast decimal(6), + votesvalid decimal(16,6), + votesinvalid decimal(16,6), + votescast decimal(16,6), entitled_users_at_stop jsonb, sequential_number integer NOT NULL, crypt_key varchar(256), @@ -865,9 +865,9 @@ CREATE TABLE option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer DEFAULT 10000, text text, - yes decimal(6), - no decimal(6), - abstain decimal(6), + yes decimal(16,6), + no decimal(16,6), + abstain decimal(16,6), poll_id integer, content_object_id varchar(100), content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -882,7 +882,7 @@ CREATE TABLE option_t ( CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight decimal(6), + weight decimal(16,6), value varchar(256), user_token varchar(256) NOT NULL, option_id integer NOT NULL, diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 5c62706d..51df2120 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1054,12 +1054,8 @@ def get_foreign_table_from_to_or_reference( "method": GenerateCodeBlocks.get_schema_simple_types, }, "float": {"pg_type": "real", "method": GenerateCodeBlocks.get_schema_simple_types}, - "decimal": { - "pg_type": string.Template("decimal(${maxLength})"), - "method": GenerateCodeBlocks.get_schema_simple_types, - }, "decimal(6)": { - "pg_type": "decimal(6)", + "pg_type": "decimal(16,6)", "method": GenerateCodeBlocks.get_schema_simple_types, }, "timestamp": { From 39c09d4da717a79c3857804f0e416083aea85b03 Mon Sep 17 00:00:00 2001 From: rrenkert Date: Mon, 7 Apr 2025 14:18:23 +0200 Subject: [PATCH 078/142] Send notifications on transaction commit (#249) --- dev/sql/schema_relational.sql | 380 ++++++++++++++++++++++++--------- dev/src/generate_sql_schema.py | 66 +++++- 2 files changed, 340 insertions(+), 106 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index df81820b..d599c688 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -4,6 +4,17 @@ -- MODELS_YML_CHECKSUM = 'b032c8ffd6e7dab1ae122c190520ce74' +-- Database parameters + +-- Do not log messages lower than WARNING +-- For client side logging this can be overwritten using +-- +-- SET client_min_messages TO NOTICE; +-- +-- to get the log messages in the client locally. +SET log_min_messages TO WARNING; + + -- Function and meta table definitions CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine @@ -48,26 +59,61 @@ begin end; $not_null_trigger$ language plpgsql; -CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ +CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ DECLARE - channel TEXT; + operation TEXT; payload TEXT; BEGIN - channel:= LOWER(TG_OP); + operation := LOWER(TG_OP); payload := TG_TABLE_NAME || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN payload = TG_TABLE_NAME || '/' || OLD.id; END IF; - PERFORM pg_notify(channel, payload); - INSERT INTO os_notify_log_t (channel, payload, xact_id, timestamp) VALUES (channel, payload, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); + RETURN NULL; -- AFTER TRIGGER needs no return +END; +$log_notify_trigger$ LANGUAGE plpgsql; + +CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ +DECLARE + pl TEXT; + body_content_text TEXT; +BEGIN + -- Running the trigger for the first time in a transaction the table is created and dropped after commiting the transaction. + -- Every next run of the trigger in this transaction raises a notice that the table exists. This is not ideal. + CREATE LOCAL TEMPORARY TABLE + IF NOT EXISTS tbl_notify_counter_tx_once ( + "id" integer NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY + ) ON COMMIT DROP; + + -- If running for the first time, select all modification notifications for the current transaction and send them to os-notify. + IF NOT EXISTS (SELECT * FROM tbl_notify_counter_tx_once) THEN + INSERT INTO tbl_notify_counter_tx_once DEFAULT VALUES; + -- Get all modifications as fqid of the current transaction and format them using a comma separated list. + SELECT array_to_string( + ARRAY ( + SELECT payload + FROM os_notify_log_t + WHERE xact_id = pg_current_xact_id()), + '","') + INTO body_content_text; + -- Concat current transaction id and formated fqid list to a json object. + pl := '{"xactId":' || + pg_current_xact_id() || + ',"fqids":["' || + body_content_text || + '"]}'; + PERFORM pg_notify('os_notify', pl); + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return END; $notify_trigger$ LANGUAGE plpgsql; CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - channel varchar(32), + operation varchar(32), payload varchar(256), xact_id xid8, timestamp timestamptz @@ -1964,98 +2010,236 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa -- Create triggers for notify -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); -CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t -FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + +CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); + /* Relation-list infos Generated: What will be generated for left field diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 51df2120..91f9e705 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -595,6 +595,17 @@ class Helper: -- Code generated. DO NOT EDIT. """ ) + FILE_TEMPLATE_PARAMETERS = dedent( + """ + -- Do not log messages lower than WARNING + -- For client side logging this can be overwritten using + -- + -- SET client_min_messages TO NOTICE; + -- + -- to get the log messages in the client locally. + SET log_min_messages TO WARNING; + """ + ) FILE_TEMPLATE_CONSTANT_DEFINITIONS = dedent( """ CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine @@ -639,26 +650,61 @@ class Helper: end; $not_null_trigger$ language plpgsql; - CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ + CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ DECLARE - channel TEXT; + operation TEXT; payload TEXT; BEGIN - channel:= LOWER(TG_OP); + operation := LOWER(TG_OP); payload := TG_TABLE_NAME || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN payload = TG_TABLE_NAME || '/' || OLD.id; END IF; - PERFORM pg_notify(channel, payload); - INSERT INTO os_notify_log_t (channel, payload, xact_id, timestamp) VALUES (channel, payload, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); + RETURN NULL; -- AFTER TRIGGER needs no return + END; + $log_notify_trigger$ LANGUAGE plpgsql; + + CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ + DECLARE + pl TEXT; + body_content_text TEXT; + BEGIN + -- Running the trigger for the first time in a transaction the table is created and dropped after commiting the transaction. + -- Every next run of the trigger in this transaction raises a notice that the table exists. This is not ideal. + CREATE LOCAL TEMPORARY TABLE + IF NOT EXISTS tbl_notify_counter_tx_once ( + "id" integer NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY + ) ON COMMIT DROP; + + -- If running for the first time, select all modification notifications for the current transaction and send them to os-notify. + IF NOT EXISTS (SELECT * FROM tbl_notify_counter_tx_once) THEN + INSERT INTO tbl_notify_counter_tx_once DEFAULT VALUES; + -- Get all modifications as fqid of the current transaction and format them using a comma separated list. + SELECT array_to_string( + ARRAY ( + SELECT payload + FROM os_notify_log_t + WHERE xact_id = pg_current_xact_id()), + '","') + INTO body_content_text; + -- Concat current transaction id and formated fqid list to a json object. + pl := '{"xactId":' || + pg_current_xact_id() || + ',"fqids":["' || + body_content_text || + '"]}'; + PERFORM pg_notify('os_notify', pl); + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return END; $notify_trigger$ LANGUAGE plpgsql; CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - channel varchar(32), + operation varchar(32), payload varchar(256), xact_id xid8, timestamp timestamptz @@ -763,8 +809,10 @@ def get_view_body_end(table_name: str, code: str) -> str: @staticmethod def get_notify_trigger(table_name: str) -> str: own_table = HelperGetNames.get_table_name(table_name) - code = f"CREATE TRIGGER modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" - code += "FOR EACH ROW EXECUTE FUNCTION notify_modified_models();" + code = f"CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" + code += "FOR EACH ROW EXECUTE FUNCTION log_modified_models();\n" + code += f"CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" + code += "DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models();\n" return code @staticmethod @@ -1132,6 +1180,8 @@ def main() -> None: with open(DESTINATION, "w") as dest: dest.write(Helper.FILE_TEMPLATE_HEADER) dest.write("-- MODELS_YML_CHECKSUM = " + repr(checksum) + "\n") + dest.write("\n\n-- Database parameters\n") + dest.write(Helper.FILE_TEMPLATE_PARAMETERS) dest.write("\n\n-- Function and meta table definitions\n") dest.write(Helper.FILE_TEMPLATE_CONSTANT_DEFINITIONS) dest.write("\n\n-- Type definitions\n") From 39a3824dadb1fd5ce53a8fe30e475b710ff3a1ea Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 7 Apr 2025 18:13:31 +0200 Subject: [PATCH 079/142] Set views in quotes. (#250) With this change, it is not necessary anymore to use `user_` and `group_`. Fixes: #243 --- dev/sql/schema_relational.sql | 72 +++++++++++++++++------------------ dev/src/helper_get_names.py | 6 +-- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index d599c688..137b995d 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1337,7 +1337,7 @@ CREATE TABLE nm_chat_group_write_group_ids_group ( -- View definitions -CREATE VIEW organization AS SELECT *, +CREATE VIEW "organization" AS SELECT *, (select array_agg(g.id) from gender_t g where g.organization_id = o.id) as gender_ids, (select array_agg(c.id) from committee_t c where c.organization_id = o.id) as committee_ids, (select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, @@ -1351,7 +1351,7 @@ CREATE VIEW organization AS SELECT *, FROM organization_t o; -CREATE VIEW user_ AS SELECT *, +CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, (select array_agg(n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, (select array_agg(n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, @@ -1363,9 +1363,9 @@ CREATE VIEW user_ AS SELECT *, (select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids FROM user_t u; -comment on column user_.committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; -CREATE VIEW meeting_user AS SELECT *, +CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, (select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion n where n.meeting_user_id = m.id) as supported_motion_ids, @@ -1380,22 +1380,22 @@ CREATE VIEW meeting_user AS SELECT *, FROM meeting_user_t m; -CREATE VIEW gender AS SELECT *, +CREATE VIEW "gender" AS SELECT *, (select array_agg(u.id) from user_t u where u.gender_id = g.id) as user_ids FROM gender_t g; -CREATE VIEW organization_tag AS SELECT *, +CREATE VIEW "organization_tag" AS SELECT *, (select array_agg(g.id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; -CREATE VIEW theme AS SELECT *, +CREATE VIEW "theme" AS SELECT *, (select o.id from organization_t o where o.theme_id = t.id) as theme_for_organization_id FROM theme_t t; -CREATE VIEW committee AS SELECT *, +CREATE VIEW "committee" AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, (select array_agg(n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, @@ -1404,9 +1404,9 @@ CREATE VIEW committee AS SELECT *, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; -comment on column committee.user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; +comment on column "committee".user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; -CREATE VIEW meeting AS SELECT *, +CREATE VIEW "meeting" AS SELECT *, (select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, (select array_agg(p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, (select array_agg(p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, @@ -1470,13 +1470,13 @@ CREATE VIEW meeting AS SELECT *, FROM meeting_t m; -CREATE VIEW structure_level AS SELECT *, +CREATE VIEW "structure_level" AS SELECT *, (select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, (select array_agg(sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids FROM structure_level_t s; -CREATE VIEW group_ AS SELECT *, +CREATE VIEW "group" AS SELECT *, (select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, @@ -1490,38 +1490,38 @@ CREATE VIEW group_ AS SELECT *, (select array_agg(n.poll_id) from nm_group_poll_ids_poll n where n.group_id = g.id) as poll_ids FROM group_t g; -comment on column group_.meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; +comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; -CREATE VIEW tag AS SELECT *, +CREATE VIEW "tag" AS SELECT *, (select array_agg(g.id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids FROM tag_t t; -CREATE VIEW agenda_item AS SELECT *, +CREATE VIEW "agenda_item" AS SELECT *, (select array_agg(ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, (select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_agenda_item_id = a.id) as tag_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_item_t a; -CREATE VIEW list_of_speakers AS SELECT *, +CREATE VIEW "list_of_speakers" AS SELECT *, (select array_agg(s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, (select array_agg(s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, (select array_agg(p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids FROM list_of_speakers_t l; -CREATE VIEW structure_level_list_of_speakers AS SELECT *, +CREATE VIEW "structure_level_list_of_speakers" AS SELECT *, (select array_agg(st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids FROM structure_level_list_of_speakers_t s; -CREATE VIEW point_of_order_category AS SELECT *, +CREATE VIEW "point_of_order_category" AS SELECT *, (select array_agg(s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids FROM point_of_order_category_t p; -CREATE VIEW topic AS SELECT *, +CREATE VIEW "topic" AS SELECT *, (select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, @@ -1530,7 +1530,7 @@ CREATE VIEW topic AS SELECT *, FROM topic_t t; -CREATE VIEW motion AS SELECT *, +CREATE VIEW "motion" AS SELECT *, (select array_agg(mt.id) from motion_t mt where mt.lead_motion_id = m.id) as amendment_ids, (select array_agg(mt.id) from motion_t mt where mt.sort_parent_id = m.id) as sort_child_ids, (select array_agg(mt.id) from motion_t mt where mt.origin_id = m.id) as derived_motion_ids, @@ -1558,20 +1558,20 @@ CREATE VIEW motion AS SELECT *, FROM motion_t m; -CREATE VIEW motion_comment_section AS SELECT *, +CREATE VIEW "motion_comment_section" AS SELECT *, (select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, (select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, (select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as write_group_ids FROM motion_comment_section_t m; -CREATE VIEW motion_category AS SELECT *, +CREATE VIEW "motion_category" AS SELECT *, (select array_agg(mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, (select array_agg(mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids FROM motion_category_t m; -CREATE VIEW motion_block AS SELECT *, +CREATE VIEW "motion_block" AS SELECT *, (select array_agg(mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, @@ -1579,7 +1579,7 @@ CREATE VIEW motion_block AS SELECT *, FROM motion_block_t m; -CREATE VIEW motion_state AS SELECT *, +CREATE VIEW "motion_state" AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, (select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, (select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state n where n.next_state_id = m.id) as previous_state_ids, @@ -1589,14 +1589,14 @@ CREATE VIEW motion_state AS SELECT *, FROM motion_state_t m; -CREATE VIEW motion_workflow AS SELECT *, +CREATE VIEW "motion_workflow" AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, (select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, (select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id FROM motion_workflow_t m; -CREATE VIEW poll AS SELECT *, +CREATE VIEW "poll" AS SELECT *, (select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, (select array_agg(n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, @@ -1604,13 +1604,13 @@ CREATE VIEW poll AS SELECT *, FROM poll_t p; -CREATE VIEW option AS SELECT *, +CREATE VIEW "option" AS SELECT *, (select p.id from poll_t p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, (select array_agg(v.id) from vote_t v where v.option_id = o.id) as vote_ids FROM option_t o; -CREATE VIEW assignment AS SELECT *, +CREATE VIEW "assignment" AS SELECT *, (select array_agg(ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, (select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, @@ -1621,19 +1621,19 @@ CREATE VIEW assignment AS SELECT *, FROM assignment_t a; -CREATE VIEW poll_candidate_list AS SELECT *, +CREATE VIEW "poll_candidate_list" AS SELECT *, (select array_agg(pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, (select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id FROM poll_candidate_list_t p; -CREATE VIEW mediafile AS SELECT *, +CREATE VIEW "mediafile" AS SELECT *, (select array_agg(mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, (select array_agg(mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids FROM mediafile_t m; -CREATE VIEW meeting_mediafile AS SELECT *, +CREATE VIEW "meeting_mediafile" AS SELECT *, (select array_agg(n.group_id) from nm_group_mmiagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, (select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, (select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, @@ -1657,9 +1657,9 @@ CREATE VIEW meeting_mediafile AS SELECT *, (select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id FROM meeting_mediafile_t m; -comment on column meeting_mediafile.inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; +comment on column "meeting_mediafile".inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; -CREATE VIEW projector AS SELECT *, +CREATE VIEW "projector" AS SELECT *, (select array_agg(pt.id) from projection_t pt where pt.current_projector_id = p.id) as current_projection_ids, (select array_agg(pt.id) from projection_t pt where pt.preview_projector_id = p.id) as preview_projection_ids, (select array_agg(pt.id) from projection_t pt where pt.history_projector_id = p.id) as history_projection_ids, @@ -1667,19 +1667,19 @@ CREATE VIEW projector AS SELECT *, FROM projector_t p; -CREATE VIEW projector_message AS SELECT *, +CREATE VIEW "projector_message" AS SELECT *, (select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_message_t p; -CREATE VIEW projector_countdown AS SELECT *, +CREATE VIEW "projector_countdown" AS SELECT *, (select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, (select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, (select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdown_t p; -CREATE VIEW chat_group AS SELECT *, +CREATE VIEW "chat_group" AS SELECT *, (select array_agg(cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, (select array_agg(n.group_id) from nm_chat_group_read_group_ids_group n where n.chat_group_id = c.id) as read_group_ids, (select array_agg(n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index ffbff67a..f7c676fc 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -95,10 +95,8 @@ def get_table_name(table_name: str) -> str: @staticmethod @max_length def get_view_name(table_name: str) -> str: - """get's the name of a view, usually the old collection name""" - if table_name in ("group", "user"): - return table_name + "_" - return table_name + """get's the name of a view. Its the collection name in quotes""" + return f'"{table_name}"' @staticmethod @max_length From bce10cf81a9ddb9423712a5627a41bc00971f3e1 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Tue, 8 Apr 2025 10:21:18 +0200 Subject: [PATCH 080/142] Add/Fix file with init data (#251) Add SQL-File to add basic data --- .github/workflows/test-sql.yml | 47 +++++++++++ dev/scripts/apply_base_data.sh | 4 + dev/sql/base_data.sql | 128 ++++++++++++++++++++++++++++++ dev/sql/example_transactional.sql | 76 ------------------ 4 files changed, 179 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/test-sql.yml create mode 100755 dev/scripts/apply_base_data.sh create mode 100644 dev/sql/base_data.sql delete mode 100644 dev/sql/example_transactional.sql diff --git a/.github/workflows/test-sql.yml b/.github/workflows/test-sql.yml new file mode 100644 index 00000000..5c21ace5 --- /dev/null +++ b/.github/workflows/test-sql.yml @@ -0,0 +1,47 @@ +name: Test SQL +on: [pull_request] +jobs: + test: + runs-on: ubuntu-latest + # TODO: What container should be used? This is from the github example + container: node:20-bookworm-slim + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: password + POSTGRES_USER: openslides + POSTGRES_DB: openslides + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Install psql + run: | + apt-get update + apt-get install --yes --no-install-recommends postgresql-client + + - name: apply schema + run: sh dev/scripts/apply_db_schema.sh + env: + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_USER: openslides + DATABASE_NAME: openslides + PGPASSWORD: password + + - name: insert init data + run: sh dev/scripts/apply_base_data.sh + env: + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_USER: openslides + DATABASE_NAME: openslides + PGPASSWORD: password diff --git a/dev/scripts/apply_base_data.sh b/dev/scripts/apply_base_data.sh new file mode 100755 index 00000000..005d3b71 --- /dev/null +++ b/dev/scripts/apply_base_data.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd "$(dirname "$0")" +psql -1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/base_data.sql diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql new file mode 100644 index 00000000..a932ed46 --- /dev/null +++ b/dev/sql/base_data.sql @@ -0,0 +1,128 @@ +-- This script can only be used for an empty database without used sequences. +INSERT INTO + theme (id, name, accent_500, primary_500, warn_500) +VALUES + (1, 'standard theme',); + +INSERT INTO + organization (name, theme_id, default_language) +VALUES + ('Intevation', 1); + +INSERT INTO + committee (id, name) +VALUES + (1, 'committee'); + +INSERT INTO + meeting ( + id, + default_group_id, + admin_group_id, + motions_default_workflow_id, + motions_default_amendment_workflow_id, + committee_id, + reference_projector_id, + name, + language + ) +VALUES + (1, 1, 2, 1, 1, 1, 1, 'meeting'); + +INSERT INTO + -- TODO Replace it with "group" after this is fixed: https://github.com/OpenSlides/openslides-meta/issues/243 + group_ (id, name, meeting_id, permissions) +VALUES + ( + 1, + 'Default', + 1, + '{ + "agenda_item.can_see", + "assignment.can_see", + "meeting.can_see_autopilot", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see" + }' + ), + (2, 'Admin', 1, DEFAULT); + +INSERT INTO + motion_workflow ( + id, + name, + sequential_number, + first_state_id, + meeting_id + ) +VALUES + (1, 'Simple Workflow', 1, 1, 1); + +INSERT INTO + motion_state ( + id, + name, + weight, + workflow_id, + meeting_id, + allow_create_poll, + allow_support, + set_workflow_timestamp, + recommendation_label, + css_class, + merge_amendment_into_final + ) +VALUES + ( + 1, + 'submitted', + 1, + 1, + 1, + true, + true, + true, + 'Submitted', + 'grey', + 'do_not_merge' + ), + ( + 2, + 'accepted', + 2, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Acceptance', + 'green', + 'do_merge' + ), + ( + 3, + 'rejected', + 3, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Rejection', + 'red', + 'do_not_merge' + ), + ( + 4, + 'not decided', + 4, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'No decision', + 'grey', + 'do_not_merge' + ); diff --git a/dev/sql/example_transactional.sql b/dev/sql/example_transactional.sql deleted file mode 100644 index 7a6a5516..00000000 --- a/dev/sql/example_transactional.sql +++ /dev/null @@ -1,76 +0,0 @@ --- this script can only be used for an empty database without used sequences -BEGIN; -INSERT INTO themeT (name, accent_500, primary_500, warn_500) values ('standard theme', 2201331, 3241878, 15754240) returning id as ret_theme_id; -INSERT INTO organizationT (name, theme_id, default_language) values ('Intevation', ret_theme_id, 'en'); - -INSERT INTO committeeT (name) VALUES ('c1'), ('c2'), ('c3'), ('c4'); --- INSERT INTO forwarding_committee_to_committee values (1, 2), (1, 3), (4, 1); - --- meeting1 in committee 1 with 2 groups, simple workflow and 1 reference projector -INSERT INTO meetingT (default_group_id, admin_group_id, - motions_default_workflow_id, motions_default_amendment_workflow_id, - motions_default_statute_amendment_workflow_id, - committee_id, reference_projector_id, name, language) - VALUES (1, 2, 1, 1, 1, 1, 1, 'meeting1', 'en'); -INSERT INTO groupT (name, meeting_id, permissions) - VALUES - ('Default', 1, '{ - "agenda_item.can_see", - "assignment.can_see", - "meeting.can_see_autopilot", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see" - }'), ('Admin', 1, DEFAULT); - -INSERT INTO motion_workflowT (name, sequential_number, first_state_id, meeting_id) - VALUES ('Simple Workflow', 1, 1, 1); -INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, allow_create_poll, allow_support, set_workflow_timestamp) - VALUES ('submitted', 1, 1, 1, true, true, true); -INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, - recommendation_label, css_class, merge_amendment_into_final) VALUES - ('accepted', 2, 1, 1, 'Acceptance', 'green', 'do_merge'), - ('rejected', 3, 1, 1, 'Rejection', 'red', 'do_not_merge'), - ('not decided', 4, 1, 1, 'No decision', 'grey', 'do_not_merge'); --- INSERT INTO motion_state_to_stateT (previous_state_id, next_state_id) VALUES --- (1, 2), (1, 3), (1,4); -INSERT INTO projectorT (name, sequential_number, meeting_id) VALUES ('Projektor 1', 1, 1); - --- -- meeting2 in committee 2 with 2 groups, simple workflow and 1 reference projector -INSERT INTO meetingT (default_group_id, admin_group_id, - motions_default_workflow_id, motions_default_amendment_workflow_id, - motions_default_statute_amendment_workflow_id, - committee_id, reference_projector_id, name, language) - VALUES (3, 4, 2, 2, 2, 2, 2, 'meeting2', 'en'); -INSERT INTO groupT (name, meeting_id, permissions) - VALUES - ('Default', 2, '{ - "agenda_item.can_see_internal", - "list_of_speakers.can_see", - "mediafile.can_see", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see", - "user.can_see" - }'), ('Admin', 2, DEFAULT); --- Update a specific cell -UPDATE groupt set permissions[3] = 'user.can_manage' where name = 'Default'; -INSERT INTO motion_workflowT (name, sequential_number, first_state_id, meeting_id) - VALUES ('Simple Workflow', 1, 5, 2); -INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, allow_create_poll, allow_support, set_workflow_timestamp) - VALUES ('submitted', 1, 2, 2, true, true, true); -INSERT INTO motion_stateT (name, weight, workflow_id, meeting_id, - recommendation_label, css_class, merge_amendment_into_final) VALUES - ('accepted', 2, 2, 2, 'Acceptance', 'green', 'do_merge'), - ('rejected', 3, 2, 2, 'Rejection', 'red', 'do_not_merge'), - ('not decided', 4, 2, 2, 'No decision', 'grey', 'do_not_merge'); --- INSERT INTO motion_state_to_state (previous_state_id, next_state_id) VALUES --- (5, 6), (5, 7), (8, 5); -INSERT INTO projectorT (name, sequential_number, meeting_id) VALUES ('Projektor 2', 1, 2); - --- -- user 1 to 4 with relations to meetings and committes as managers --- INSERT INTO userT (username) values ('u1_m1'), ('u2_m2'), ('u3_c1manager'), ('u4_m1m2c1managerc3manager'); --- INSERT INTO group_to_user (user_id, group_id) values (1, 1), (2, 4), (4, 1), (4, 3); --- INSERT INTO committee_to_user (user_id, committee_id) values (3, 1), (4, 1), (4,3); - -COMMIT; \ No newline at end of file From 0195595c72ba47ae328df455dd284541150a9521 Mon Sep 17 00:00:00 2001 From: Kasimir Klinger <77665830+LinKaKling@users.noreply.github.com> Date: Wed, 9 Apr 2025 10:34:19 +0200 Subject: [PATCH 081/142] remove _t from table name in notification to get correct fqid (#252) * remove _t from table name in notification to get correct fqid * small change for successful CI check with black --------- Co-authored-by: Kasimir Klinger --- dev/sql/schema_relational.sql | 110 +++++++++++++++++---------------- dev/src/generate_sql_schema.py | 9 ++- 2 files changed, 62 insertions(+), 57 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 137b995d..7d3252a7 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'b032c8ffd6e7dab1ae122c190520ce74' +-- MODELS_YML_CHECKSUM = '4e5ae1fdf08fa7cefceb58d0dff6b957' -- Database parameters @@ -61,13 +61,15 @@ $not_null_trigger$ language plpgsql; CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ DECLARE + escaped_table_name varchar; operation TEXT; payload TEXT; BEGIN + escaped_table_name := TG_ARGV[0]; operation := LOWER(TG_OP); - payload := TG_TABLE_NAME || '/' || NEW.id; + payload := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - payload = TG_TABLE_NAME || '/' || OLD.id; + payload = escaped_table_name || '/' || OLD.id; END IF; INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); @@ -137,7 +139,7 @@ CREATE TABLE organization_t ( enable_chat boolean, limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, - default_language varchar(256) NOT NULL CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), + default_language varchar(256) CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', require_duplicate_from boolean, enable_anonymous boolean, saml_enabled boolean, @@ -251,7 +253,7 @@ CREATE TABLE theme_t ( accent_300 varchar(7) CHECK (accent_300 is null or accent_300 ~* '^#[a-f0-9]{6}$'), accent_400 varchar(7) CHECK (accent_400 is null or accent_400 ~* '^#[a-f0-9]{6}$'), accent_50 varchar(7) CHECK (accent_50 is null or accent_50 ~* '^#[a-f0-9]{6}$'), - accent_500 varchar(7) CHECK (accent_500 is null or accent_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + accent_500 varchar(7) CHECK (accent_500 is null or accent_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#2196f3', accent_600 varchar(7) CHECK (accent_600 is null or accent_600 ~* '^#[a-f0-9]{6}$'), accent_700 varchar(7) CHECK (accent_700 is null or accent_700 ~* '^#[a-f0-9]{6}$'), accent_800 varchar(7) CHECK (accent_800 is null or accent_800 ~* '^#[a-f0-9]{6}$'), @@ -265,7 +267,7 @@ CREATE TABLE theme_t ( primary_300 varchar(7) CHECK (primary_300 is null or primary_300 ~* '^#[a-f0-9]{6}$'), primary_400 varchar(7) CHECK (primary_400 is null or primary_400 ~* '^#[a-f0-9]{6}$'), primary_50 varchar(7) CHECK (primary_50 is null or primary_50 ~* '^#[a-f0-9]{6}$'), - primary_500 varchar(7) CHECK (primary_500 is null or primary_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + primary_500 varchar(7) CHECK (primary_500 is null or primary_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', primary_600 varchar(7) CHECK (primary_600 is null or primary_600 ~* '^#[a-f0-9]{6}$'), primary_700 varchar(7) CHECK (primary_700 is null or primary_700 ~* '^#[a-f0-9]{6}$'), primary_800 varchar(7) CHECK (primary_800 is null or primary_800 ~* '^#[a-f0-9]{6}$'), @@ -279,7 +281,7 @@ CREATE TABLE theme_t ( warn_300 varchar(7) CHECK (warn_300 is null or warn_300 ~* '^#[a-f0-9]{6}$'), warn_400 varchar(7) CHECK (warn_400 is null or warn_400 ~* '^#[a-f0-9]{6}$'), warn_50 varchar(7) CHECK (warn_50 is null or warn_50 ~* '^#[a-f0-9]{6}$'), - warn_500 varchar(7) CHECK (warn_500 is null or warn_500 ~* '^#[a-f0-9]{6}$') NOT NULL, + warn_500 varchar(7) CHECK (warn_500 is null or warn_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#f06400', warn_600 varchar(7) CHECK (warn_600 is null or warn_600 ~* '^#[a-f0-9]{6}$'), warn_700 varchar(7) CHECK (warn_700 is null or warn_700 ~* '^#[a-f0-9]{6}$'), warn_800 varchar(7) CHECK (warn_800 is null or warn_800 ~* '^#[a-f0-9]{6}$'), @@ -326,7 +328,7 @@ CREATE TABLE meeting_t ( end_time timestamptz, locked_from_inside boolean, imported_at timestamptz, - language varchar(256) NOT NULL CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')), + language varchar(256) CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', jitsi_domain varchar(256), jitsi_room_name varchar(256), jitsi_room_password varchar(256), @@ -2011,232 +2013,232 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa -- Create triggers for notify CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('user'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_user'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('gender'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization_tag'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('theme'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('committee'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('group'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('personal_note'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('tag'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('agenda_item'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('list_of_speakers'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level_list_of_speakers'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('point_of_order_category'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('speaker'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('topic'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_submitter'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_working_group_speaker'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment_section'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_category'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_block'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_change_recommendation'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_state'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_workflow'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('option'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment_candidate'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_mediafile'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projection'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_message'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_countdown'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_group'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_message'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('action_worker'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models(); +FOR EACH ROW EXECUTE FUNCTION log_modified_models('import_preview'); CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 91f9e705..5bd49745 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -652,13 +652,15 @@ class Helper: CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ DECLARE + escaped_table_name varchar; operation TEXT; payload TEXT; BEGIN + escaped_table_name := TG_ARGV[0]; operation := LOWER(TG_OP); - payload := TG_TABLE_NAME || '/' || NEW.id; + payload := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - payload = TG_TABLE_NAME || '/' || OLD.id; + payload = escaped_table_name || '/' || OLD.id; END IF; INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); @@ -809,8 +811,9 @@ def get_view_body_end(table_name: str, code: str) -> str: @staticmethod def get_notify_trigger(table_name: str) -> str: own_table = HelperGetNames.get_table_name(table_name) + escaped_table_name = "'" + table_name + "'" code = f"CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" - code += "FOR EACH ROW EXECUTE FUNCTION log_modified_models();\n" + code += f"FOR EACH ROW EXECUTE FUNCTION log_modified_models({escaped_table_name});\n" code += f"CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" code += "DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models();\n" return code From 8c03c46acfda73725cdb839efd003adfa04f3c85 Mon Sep 17 00:00:00 2001 From: rrenkert Date: Wed, 9 Apr 2025 16:57:51 +0200 Subject: [PATCH 082/142] update trigger and functions (#254) --- dev/sql/schema_relational.sql | 225 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 45 +++---- 2 files changed, 124 insertions(+), 146 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 7d3252a7..041f8e68 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -59,54 +59,43 @@ begin end; $not_null_trigger$ language plpgsql; -CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ +CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; operation TEXT; - payload TEXT; + fqid TEXT; BEGIN escaped_table_name := TG_ARGV[0]; operation := LOWER(TG_OP); - payload := escaped_table_name || '/' || NEW.id; + fqid := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - payload = escaped_table_name || '/' || OLD.id; + fqid = escaped_table_name || '/' || OLD.id; END IF; - INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) VALUES (operation, fqid, pg_current_xact_id(), 'now'); RETURN NULL; -- AFTER TRIGGER needs no return END; -$log_notify_trigger$ LANGUAGE plpgsql; +$log_modified_trigger$ LANGUAGE plpgsql; -CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ +CREATE FUNCTION notify_transaction_end() RETURNS trigger AS $notify_trigger$ DECLARE - pl TEXT; + payload TEXT; body_content_text TEXT; BEGIN - -- Running the trigger for the first time in a transaction the table is created and dropped after commiting the transaction. - -- Every next run of the trigger in this transaction raises a notice that the table exists. This is not ideal. + -- Running the trigger for the first time in a transaction creates the table and after commiting the transaction the table is dropped. + -- Every next run of the trigger in this transaction raises a notice that the table exists. Setting the log_min_messages to notice increases the noise because of such messages. CREATE LOCAL TEMPORARY TABLE IF NOT EXISTS tbl_notify_counter_tx_once ( "id" integer NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY ) ON COMMIT DROP; - -- If running for the first time, select all modification notifications for the current transaction and send them to os-notify. + -- If running for the first time, the transaction id is send via os_notify. IF NOT EXISTS (SELECT * FROM tbl_notify_counter_tx_once) THEN INSERT INTO tbl_notify_counter_tx_once DEFAULT VALUES; - -- Get all modifications as fqid of the current transaction and format them using a comma separated list. - SELECT array_to_string( - ARRAY ( - SELECT payload - FROM os_notify_log_t - WHERE xact_id = pg_current_xact_id()), - '","') - INTO body_content_text; - -- Concat current transaction id and formated fqid list to a json object. - pl := '{"xactId":' || + payload := '{"xactId":' || pg_current_xact_id() || - ',"fqids":["' || - body_content_text || - '"]}'; - PERFORM pg_notify('os_notify', pl); + '}'; + PERFORM pg_notify('os_notify', payload); END IF; RETURN NULL; -- AFTER TRIGGER needs no return @@ -116,7 +105,7 @@ $notify_trigger$ LANGUAGE plpgsql; CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, operation varchar(32), - payload varchar(256), + fqid varchar(256), xact_id xid8, timestamp timestamptz ); @@ -2014,233 +2003,233 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa -- Create triggers for notify CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('user'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_user'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('gender'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gender_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization_tag'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('theme'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON theme_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('committee'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON committee_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('group'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('personal_note'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('tag'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON tag_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('agenda_item'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('list_of_speakers'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level_list_of_speakers'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('point_of_order_category'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('speaker'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON speaker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('topic'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON topic_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_submitter'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_working_group_speaker'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment_section'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_category'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_block'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_change_recommendation'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_state'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_state_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_workflow'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('option'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON option_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment_candidate'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_mediafile'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projection'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projection_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_message'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_countdown'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_group'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_message'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('action_worker'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('import_preview'); -CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models(); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); /* Relation-list infos diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 5bd49745..851eb737 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -650,54 +650,43 @@ class Helper: end; $not_null_trigger$ language plpgsql; - CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_notify_trigger$ + CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; operation TEXT; - payload TEXT; + fqid TEXT; BEGIN escaped_table_name := TG_ARGV[0]; operation := LOWER(TG_OP); - payload := escaped_table_name || '/' || NEW.id; + fqid := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - payload = escaped_table_name || '/' || OLD.id; + fqid = escaped_table_name || '/' || OLD.id; END IF; - INSERT INTO os_notify_log_t (operation, payload, xact_id, timestamp) VALUES (operation, payload, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) VALUES (operation, fqid, pg_current_xact_id(), 'now'); RETURN NULL; -- AFTER TRIGGER needs no return END; - $log_notify_trigger$ LANGUAGE plpgsql; + $log_modified_trigger$ LANGUAGE plpgsql; - CREATE FUNCTION notify_modified_models() RETURNS trigger AS $notify_trigger$ + CREATE FUNCTION notify_transaction_end() RETURNS trigger AS $notify_trigger$ DECLARE - pl TEXT; + payload TEXT; body_content_text TEXT; BEGIN - -- Running the trigger for the first time in a transaction the table is created and dropped after commiting the transaction. - -- Every next run of the trigger in this transaction raises a notice that the table exists. This is not ideal. + -- Running the trigger for the first time in a transaction creates the table and after commiting the transaction the table is dropped. + -- Every next run of the trigger in this transaction raises a notice that the table exists. Setting the log_min_messages to notice increases the noise because of such messages. CREATE LOCAL TEMPORARY TABLE IF NOT EXISTS tbl_notify_counter_tx_once ( "id" integer NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY ) ON COMMIT DROP; - -- If running for the first time, select all modification notifications for the current transaction and send them to os-notify. + -- If running for the first time, the transaction id is send via os_notify. IF NOT EXISTS (SELECT * FROM tbl_notify_counter_tx_once) THEN INSERT INTO tbl_notify_counter_tx_once DEFAULT VALUES; - -- Get all modifications as fqid of the current transaction and format them using a comma separated list. - SELECT array_to_string( - ARRAY ( - SELECT payload - FROM os_notify_log_t - WHERE xact_id = pg_current_xact_id()), - '","') - INTO body_content_text; - -- Concat current transaction id and formated fqid list to a json object. - pl := '{"xactId":' || + payload := '{"xactId":' || pg_current_xact_id() || - ',"fqids":["' || - body_content_text || - '"]}'; - PERFORM pg_notify('os_notify', pl); + '}'; + PERFORM pg_notify('os_notify', payload); END IF; RETURN NULL; -- AFTER TRIGGER needs no return @@ -707,7 +696,7 @@ class Helper: CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, operation varchar(32), - payload varchar(256), + fqid varchar(256), xact_id xid8, timestamp timestamptz ); @@ -814,8 +803,8 @@ def get_notify_trigger(table_name: str) -> str: escaped_table_name = "'" + table_name + "'" code = f"CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" code += f"FOR EACH ROW EXECUTE FUNCTION log_modified_models({escaped_table_name});\n" - code += f"CREATE CONSTRAINT TRIGGER notify_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" - code += "DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_modified_models();\n" + code += f"CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" + code += "DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end();\n" return code @staticmethod From 110616460863f292922e7426e668c9e08e30d842 Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Fri, 11 Apr 2025 14:30:21 +0200 Subject: [PATCH 083/142] Generate views for all collections (#255) --- dev/sql/schema_relational.sql | 42 +++++++++++++++++++++++++++++ dev/src/generate_sql_schema.py | 19 +++++++++----- dev/src/helper_get_names.py | 48 +++++++++++++++++----------------- 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 041f8e68..e2013e1a 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1483,6 +1483,9 @@ FROM group_t g; comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; +CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; + + CREATE VIEW "tag" AS SELECT *, (select array_agg(g.id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids FROM tag_t t; @@ -1512,6 +1515,9 @@ CREATE VIEW "point_of_order_category" AS SELECT *, FROM point_of_order_category_t p; +CREATE VIEW "speaker" AS SELECT * FROM speaker_t s; + + CREATE VIEW "topic" AS SELECT *, (select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, @@ -1549,6 +1555,18 @@ CREATE VIEW "motion" AS SELECT *, FROM motion_t m; +CREATE VIEW "motion_submitter" AS SELECT * FROM motion_submitter_t m; + + +CREATE VIEW "motion_editor" AS SELECT * FROM motion_editor_t m; + + +CREATE VIEW "motion_working_group_speaker" AS SELECT * FROM motion_working_group_speaker_t m; + + +CREATE VIEW "motion_comment" AS SELECT * FROM motion_comment_t m; + + CREATE VIEW "motion_comment_section" AS SELECT *, (select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, (select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, @@ -1570,6 +1588,9 @@ CREATE VIEW "motion_block" AS SELECT *, FROM motion_block_t m; +CREATE VIEW "motion_change_recommendation" AS SELECT * FROM motion_change_recommendation_t m; + + CREATE VIEW "motion_state" AS SELECT *, (select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, (select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, @@ -1601,6 +1622,9 @@ CREATE VIEW "option" AS SELECT *, FROM option_t o; +CREATE VIEW "vote" AS SELECT * FROM vote_t v; + + CREATE VIEW "assignment" AS SELECT *, (select array_agg(ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, (select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, @@ -1612,12 +1636,18 @@ CREATE VIEW "assignment" AS SELECT *, FROM assignment_t a; +CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; + + CREATE VIEW "poll_candidate_list" AS SELECT *, (select array_agg(pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, (select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id FROM poll_candidate_list_t p; +CREATE VIEW "poll_candidate" AS SELECT * FROM poll_candidate_t p; + + CREATE VIEW "mediafile" AS SELECT *, (select array_agg(mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, (select array_agg(mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids @@ -1658,6 +1688,9 @@ CREATE VIEW "projector" AS SELECT *, FROM projector_t p; +CREATE VIEW "projection" AS SELECT * FROM projection_t p; + + CREATE VIEW "projector_message" AS SELECT *, (select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_message_t p; @@ -1677,6 +1710,15 @@ CREATE VIEW "chat_group" AS SELECT *, FROM chat_group_t c; +CREATE VIEW "chat_message" AS SELECT * FROM chat_message_t c; + + +CREATE VIEW "action_worker" AS SELECT * FROM action_worker_t a; + + +CREATE VIEW "import_preview" AS SELECT * FROM import_preview_t i; + + -- Alter table relations ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 851eb737..e710b4d6 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -148,9 +148,10 @@ def generate_the_code( table_name_code += code + "\n" if code := schema_zone_texts["undecided"]: table_name_code += Helper.get_undecided_all(table_name, code) - if code := schema_zone_texts["view"]: - view_name_code += Helper.get_view_head(table_name) - view_name_code += Helper.get_view_body_end(table_name, code) + view_name_code += Helper.get_view_head(table_name) + view_name_code += Helper.get_view_body_end( + table_name, schema_zone_texts.get("view", "") + ) if code := schema_zone_texts["post_view"]: view_name_code += code if code := schema_zone_texts["alter_table_final"]: @@ -787,13 +788,17 @@ def get_table_body_end(code: str) -> str: @staticmethod def get_view_head(table_name: str) -> str: - return ( - f"\nCREATE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *,\n" - ) + return f"\nCREATE VIEW {HelperGetNames.get_view_name(table_name)} AS SELECT *" @staticmethod def get_view_body_end(table_name: str, code: str) -> str: - code = code[:-2] + "\n" # last attribute line without ",", but with "\n" + # change the code only if there is + if code: + # comma and "\n" for the header + # last attribute line without ",", but with "\n" + code = ",\n" + code[:-2] + "\n" + else: + code = " " code += f"FROM {HelperGetNames.get_table_name(table_name)} {Helper.get_table_letter(table_name)};\n\n" return code diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index f7c676fc..56b53453 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -212,7 +212,7 @@ class InternalHelper: @classmethod def read_models_yml(cls, file: str) -> tuple[dict[str, Any], str]: - """method reads modesl.yml from file or web and returns MODELS and it's checksum""" + """method reads models.yml from file or web and returns MODELS and it's checksum""" if os.path.isfile(file): with open(file, "rb") as x: models_yml = x.read() @@ -264,7 +264,7 @@ def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: @staticmethod def get_foreign_key_table_column(reference: str | None) -> tuple[str, str]: """ - Returns a tuple (table_name, field_name) gotten from "reference"-attribut + Returns a tuple (table_name, field_name) gotten from "reference"-attribute """ if reference: result = InternalHelper.ref_compiled.search(reference) @@ -293,7 +293,7 @@ def get_models(cls, collection: str, field: str) -> dict[str, Any]: def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: """ Returns - - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generatic-relation, r=reference, t=to, s=sql, R=required) + - string with cardinality string (1, 1G, n or nG= Cardinality, G=Generic-relation, r=reference, t=to, s=sql, R=required) - string with error message or empty string if no error """ error = "" @@ -305,14 +305,14 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: reference = bool(field.get("reference")) # general rules of inconsistent field descriptions on field level - if reference and not to: # temporaray rule to keep all to-attributes - error = "Field with reference temporarely needs also to-attribute\n" + if reference and not to: # temporary rule to keep all to-attributes + error = "Field with reference temporarily needs also to-attribute\n" elif field.get("sql") == "": error = "sql attribute may not be empty\n" elif required and sql: error = "Field with attribute sql cannot be required\n" elif not (to or reference): - error = "Relation field must have `to` or `reference` attribut set\n" + error = "Relation field must have `to` or `reference` attribute set\n" elif field["type"] == "generic-relation-list" and required: error = "generic-relation-list cannot be required: not implemented\n" @@ -332,7 +332,7 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: result += "r" if ( to and not reference - ): # to with reference only for temporaray backup compatibility in backend relation-handling + ): # to with reference only for temporary backup compatibility in backend relation-handling result += "t" if required: result += "R" @@ -344,7 +344,7 @@ def get_cardinality(field_all: TableFieldType) -> tuple[str, str]: @staticmethod def generate_field_or_sql_decision( - own: TableFieldType, own_c: str, foreign: TableFieldType, foreign_c: str + own: TableFieldType, own_card: str, foreign: TableFieldType, foreign_card: str ) -> tuple[FieldSqlErrorType, bool, str]: """ Returns: @@ -376,7 +376,7 @@ def generate_field_or_sql_decision( ("nts", "nts"): (FieldSqlErrorType.SQL, False), } - foreign_c_replacement_list: list[str] = [ + foreign_card_replacement_list: list[str] = [ "1Gr", "1GrR", "1r", @@ -388,12 +388,12 @@ def generate_field_or_sql_decision( primary: bool | str | None error = "" - if own_c in foreign_c_replacement_list: - foreign_c = "" + if own_card in foreign_card_replacement_list: + foreign_card = "" - state, primary = decision_list.get((own_c, foreign_c), (None, None)) + state, primary = decision_list.get((own_card, foreign_card), (None, None)) if state is None: - error = f"Type combination not implemented: {own_c}:{foreign_c} on field {own.collectionfield}\n" + error = f"Type combination not implemented: {own_card}:{foreign_card} on field {own.collectionfield}\n" state = FieldSqlErrorType.ERROR elif primary == "primary_decide_alphabetical": primary = ( @@ -409,12 +409,12 @@ def check_relation_definitions( ) -> tuple[FieldSqlErrorType, bool, str, str]: """ Decides for the own-field, - - whether it is a field, a sql-expression or is there an error + - whether it is a field, an sql-expression or if there is an error - relation-list and generic-relation-list are always sql-expressions. - True significates, that it is the pimary that creates the intermediate table + True signifies that it is the primary that creates the intermediate table. Also checks relational behaviour and produces the informative relation line and in - case of an error an error text + case of an error an error text. Returns: - field, sql, error => enum FieldSqlErrorType @@ -423,13 +423,13 @@ def check_relation_definitions( - error line if error else empty string """ error = "" - own_c, tmp_error = InternalHelper.get_cardinality(own_field) + own_card, tmp_error = InternalHelper.get_cardinality(own_field) error = error or tmp_error - foreigns_c = [] + foreign_card = [] foreign_collectionfields = [] for foreign_field in foreign_fields: foreign_c, tmp_error = InternalHelper.get_cardinality(foreign_field) - foreigns_c.append(foreign_c) + foreign_card.append(foreign_c) error = error or tmp_error foreign_collectionfields.append(foreign_field.collectionfield) @@ -441,23 +441,23 @@ def check_relation_definitions( if i == 0: state, primary, error = ( InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] + own_field, own_card, foreign_field, foreign_card[i] ) ) else: statex, primaryx, error = ( InternalHelper.generate_field_or_sql_decision( - own_field, own_c, foreign_field, foreigns_c[i] + own_field, own_card, foreign_field, foreign_card[i] ) ) if not error and (statex != state or primaryx != primary): - error = f"Error in generation for generic collectionfield '{own_field.collectionfield}'" + error = f"Error in generation for generic collection field '{own_field.collectionfield}'" if error: state = FieldSqlErrorType.ERROR break state_text = "***" if state == FieldSqlErrorType.ERROR else state.name - text = f"{state_text} {own_c}:{','.join(foreigns_c)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" + text = f"{state_text} {own_card}:{','.join(foreign_card)} => {own_field.collectionfield}:-> {','.join(foreign_collectionfields)}\n" if state == FieldSqlErrorType.ERROR: text += f" {error}" return state, primary, text, error @@ -470,7 +470,7 @@ def get_definitions_from_foreign_list( """ used for generic_relation with multiple foreign relations """ - # temporarely allowed + # temporarily allowed # if to and reference: # raise Exception( # f"Field {table}/{field}: On generic-relation fields it is not allowed to use 'to' and 'reference' for 1 field" From 0eb36c4fa7529e845bed10ff407eecde7df2fe2f Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 28 May 2025 11:30:01 +0200 Subject: [PATCH 084/142] [rel-db] Initially defer and use correct field in nm gm (#263) * initially defer nm and gm relations * use correct field for generic nm relations * introduce intermediate column for TableFieldType --- dev/sql/schema_relational.sql | 112 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 18 +++--- dev/src/helper_get_names.py | 5 +- 3 files changed, 69 insertions(+), 66 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index e2013e1a..37ccc79b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1168,160 +1168,160 @@ CREATE TABLE import_preview_t ( -- Intermediate table definitions CREATE TABLE nm_meeting_user_supported_motion_ids_motion ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), - motion_id integer NOT NULL REFERENCES motion_t (id), + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, + motion_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, motion_id) ); CREATE TABLE nm_meeting_user_structure_level_ids_structure_level ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), - structure_level_id integer NOT NULL REFERENCES structure_level_t (id), + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, + structure_level_id integer NOT NULL REFERENCES structure_level_t (id) INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, structure_level_id) ); CREATE TABLE gm_organization_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id), + organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, - tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id), - tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id), + tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id) INITIALLY DEFERRED, + tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id) INITIALLY DEFERRED, CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); CREATE TABLE nm_committee_user_ids_user ( - committee_id integer NOT NULL REFERENCES committee_t (id), - user_id integer NOT NULL REFERENCES user_t (id), + committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); CREATE TABLE nm_committee_manager_ids_user ( - committee_id integer NOT NULL REFERENCES committee_t (id), - user_id integer NOT NULL REFERENCES user_t (id), + committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); CREATE TABLE nm_committee_forward_to_committee_ids_committee ( - forward_to_committee_id integer NOT NULL REFERENCES committee_t (id), - receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id), + forward_to_committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, + receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); CREATE TABLE nm_meeting_present_user_ids_user ( - meeting_id integer NOT NULL REFERENCES meeting_t (id), - user_id integer NOT NULL REFERENCES user_t (id), + meeting_id integer NOT NULL REFERENCES meeting_t (id) INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, PRIMARY KEY (meeting_id, user_id) ); CREATE TABLE nm_group_meeting_user_ids_meeting_user ( - group_id integer NOT NULL REFERENCES group_t (id), - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_user_id) ); CREATE TABLE nm_group_mmagi_meeting_mediafile ( - group_id integer NOT NULL REFERENCES group_t (id), - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); CREATE TABLE nm_group_mmiagi_meeting_mediafile ( - group_id integer NOT NULL REFERENCES group_t (id), - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section ( - group_id integer NOT NULL REFERENCES group_t (id), - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section ( - group_id integer NOT NULL REFERENCES group_t (id), - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); CREATE TABLE nm_group_poll_ids_poll ( - group_id integer NOT NULL REFERENCES group_t (id), - poll_id integer NOT NULL REFERENCES poll_t (id), + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + poll_id integer NOT NULL REFERENCES poll_t (id) INITIALLY DEFERRED, PRIMARY KEY (group_id, poll_id) ); CREATE TABLE gm_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - tag_id integer NOT NULL REFERENCES tag_t(id), + tag_id integer NOT NULL REFERENCES tag_t(id) INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, - tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id), - tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id), - tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), + tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id) INITIALLY DEFERRED, + tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) INITIALLY DEFERRED, + tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); CREATE TABLE nm_motion_all_derived_motion_ids_motion ( - all_derived_motion_id integer NOT NULL REFERENCES motion_t (id), - all_origin_id integer NOT NULL REFERENCES motion_t (id), + all_derived_motion_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, + all_origin_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, PRIMARY KEY (all_derived_motion_id, all_origin_id) ); CREATE TABLE nm_motion_identical_motion_ids_motion ( - identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id), - identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id), + identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, + identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); CREATE TABLE gm_motion_state_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motion_t(id), + motion_id integer NOT NULL REFERENCES motion_t(id) INITIALLY DEFERRED, state_extension_reference_id varchar(100) NOT NULL, - state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), + state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); CREATE TABLE gm_motion_recommendation_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motion_t(id), + motion_id integer NOT NULL REFERENCES motion_t(id) INITIALLY DEFERRED, recommendation_extension_reference_id varchar(100) NOT NULL, - recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), + recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); CREATE TABLE nm_motion_state_next_state_ids_motion_state ( - next_state_id integer NOT NULL REFERENCES motion_state_t (id), - previous_state_id integer NOT NULL REFERENCES motion_state_t (id), + next_state_id integer NOT NULL REFERENCES motion_state_t (id) INITIALLY DEFERRED, + previous_state_id integer NOT NULL REFERENCES motion_state_t (id) INITIALLY DEFERRED, PRIMARY KEY (next_state_id, previous_state_id) ); CREATE TABLE nm_poll_voted_ids_user ( - poll_id integer NOT NULL REFERENCES poll_t (id), - user_id integer NOT NULL REFERENCES user_t (id), + poll_id integer NOT NULL REFERENCES poll_t (id) INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, PRIMARY KEY (poll_id, user_id) ); CREATE TABLE gm_meeting_mediafile_attachment_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id), + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED, attachment_id varchar(100) NOT NULL, - attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id), - attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id), - attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id), + attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, + attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id) INITIALLY DEFERRED, + attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) INITIALLY DEFERRED, CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); CREATE TABLE nm_chat_group_read_group_ids_group ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id), - group_id integer NOT NULL REFERENCES group_t (id), + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); CREATE TABLE nm_chat_group_write_group_ids_group ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id), - group_id integer NOT NULL REFERENCES group_t (id), + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); @@ -1377,7 +1377,7 @@ FROM gender_t g; CREATE VIEW "organization_tag" AS SELECT *, -(select array_agg(g.id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids +(select array_agg(g.tagged_id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; @@ -1487,7 +1487,7 @@ CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; CREATE VIEW "tag" AS SELECT *, -(select array_agg(g.id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids +(select array_agg(g.tagged_id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids FROM tag_t t; @@ -1534,9 +1534,9 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion n where n.all_derived_motion_id = m.id) as all_origin_ids, (select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion n where n.all_origin_id = m.id) as all_derived_motion_ids, (select array_cat((select array_agg(n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, -(select array_agg(g.id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.state_extension_reference_id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, (select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, -(select array_agg(g.id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, (select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_ids g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, (select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion n where n.motion_id = m.id) as supporter_meeting_user_ids, @@ -1659,7 +1659,7 @@ CREATE VIEW "meeting_mediafile" AS SELECT *, (select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, (select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, (select array_agg(p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, -(select array_agg(g.id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, +(select array_agg(g.attachment_id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, (select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index e710b4d6..13a8c381 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -393,7 +393,9 @@ def get_relation_list_type( foreign_table_name = HelperGetNames.get_nm_table_name( own_table_field, foreign_table_field ) - foreign_table_column = foreign_table_field.column[:-1] + foreign_table_column = ( + foreign_table_field.intermediate_column + ) else: own_ref_column = own_table_field.ref_column foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" @@ -410,7 +412,7 @@ def get_relation_list_type( foreign_table_field ) foreign_table_column = ( - f"{foreign_table_column[:-1]}_{table_name}_id" + f"{foreign_table_field.intermediate_column}_{table_name}_id" ) elif type_ == "relation" or foreign_table_field_ref_id: own_ref_column = own_table_field.ref_column @@ -578,7 +580,7 @@ def get_generic_relation_list_type( own_table_field.ref_column, gm_foreign_table, f"{own_table_field.table}_{own_table_field.ref_column}", - own_table_field.ref_column, + own_table_field.intermediate_column, ) if comment := fdata.get("description"): text["post_view"] += Helper.get_post_view_comment( @@ -710,8 +712,8 @@ class Helper: dedent( """ CREATE TABLE ${table_name} ( - ${field1} integer NOT NULL REFERENCES ${table1} (id), - ${field2} integer NOT NULL REFERENCES ${table2} (id), + ${field1} integer NOT NULL REFERENCES ${table1} (id) INITIALLY DEFERRED, + ${field2} integer NOT NULL REFERENCES ${table2} (id) INITIALLY DEFERRED, PRIMARY KEY (${list_of_keys}) ); """ @@ -722,7 +724,7 @@ class Helper: """ CREATE TABLE ${table_name} ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}), + ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}) INITIALLY DEFERRED, ${own_table_column} varchar(100) NOT NULL, ${foreign_table_ref_lines} CONSTRAINT ${valid_constraint_name} CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), @@ -732,7 +734,7 @@ class Helper: ) ) GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( - " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_view_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}(id)," + " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_view_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}(id) INITIALLY DEFERRED," ) RELATION_LIST_AGENDA = dedent( @@ -932,7 +934,7 @@ def get_gm_table_for_gm_nm_relation_lists( + "')" ) foreign_table_ref_lines = [] - own_table_column = own_table_field.column[:-1] + own_table_column = own_table_field.intermediate_column for foreign_table_field in foreign_table_fields: foreign_table_name = foreign_table_field.table subst_dict = { diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 56b53453..763ad9ff 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -21,6 +21,7 @@ def __init__( ): self.table = table self.column = column + self.intermediate_column = column[:-1] self.field_def: dict[str, Any] = field_def or {} self.ref_column = ref_column @@ -71,7 +72,7 @@ def wrapper(*args, **kwargs) -> str: # type:ignore name = func(*args, **kwargs) assert ( len(name) <= HelperGetNames.MAX_LEN - ), f"Generated name '{name}' to long in function {func}!" + ), f"Name '{name}' generated too long in function {func}!" return name return wrapper @@ -123,7 +124,7 @@ def get_field_in_n_m_relation_list( otherwise the related tables names are used """ if own_table_field.table == foreign_table_name: - return own_table_field.column[:-1] + return own_table_field.intermediate_column else: return f"{own_table_field.table}_id" From 5b903a6fa25550863c5cfb5a9951ee2af75b8309 Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 11 Jun 2025 10:20:41 +0200 Subject: [PATCH 085/142] Add cascading delete to intermediate tables (#271) * merge upstream main * add cascading delete on intermediate tables --------- Co-authored-by: LinKaKling --- .github/workflows/create-prs-for-updates.yml | 3 - dev/requirements.txt | 12 +- dev/sql/schema_relational.sql | 128 +++++++++++-------- dev/src/db_utils.py | 6 +- dev/src/generate_sql_schema.py | 8 +- models.yml | 36 ++++++ 6 files changed, 124 insertions(+), 69 deletions(-) diff --git a/.github/workflows/create-prs-for-updates.yml b/.github/workflows/create-prs-for-updates.yml index d859c8c2..15a83ac6 100644 --- a/.github/workflows/create-prs-for-updates.yml +++ b/.github/workflows/create-prs-for-updates.yml @@ -20,9 +20,6 @@ jobs: strategy: matrix: include: - - repository: openslides-autoupdate-service - assignee: ostcar - setup-action: setup-autoupdate-pr - repository: openslides-go assignee: ostcar setup-action: setup-autoupdate-pr diff --git a/dev/requirements.txt b/dev/requirements.txt index 48b730a3..f5742ee3 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -1,10 +1,10 @@ autoflake==2.3.1 black==25.1.0 -flake8==7.1.2 +flake8==7.2.0 isort==6.0.1 -mypy==1.15.0 -pytest==8.3.5 -pyupgrade==3.19.1 +mypy==1.16.0 +pytest==8.4.0 +pyupgrade==3.20.0 pyyaml==6.0.2 simplejson==3.20.1 debugpy==1.8.0 @@ -13,6 +13,6 @@ psycopg[binary]==3.2.6 python-sql==1.5.1 # typing -types-PyYAML==6.0.12.20241230 -types-simplejson==3.20.0.20250318 +types-PyYAML==6.0.12.20250516 +types-simplejson==3.20.0.20250326 types-requests==2.31.0.20240125 diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 37ccc79b..7f4adcf9 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '4e5ae1fdf08fa7cefceb58d0dff6b957' +-- MODELS_YML_CHECKSUM = '849102a66a0fdb700779571c3d949b36' -- Database parameters @@ -185,8 +185,10 @@ CREATE TABLE user_t ( last_email_sent timestamptz, is_demo_user boolean, last_login timestamptz, + guest boolean, gender_id integer, organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), + home_committee_id integer, meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -295,6 +297,7 @@ CREATE TABLE committee_t ( description text, external_id varchar(256), default_meeting_id integer, + parent_id integer, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -689,6 +692,7 @@ CREATE TABLE motion_t ( start_line_number integer CONSTRAINT minimum_start_line_number CHECK (start_line_number >= 1) DEFAULT 1, forwarded timestamptz, additional_submitter varchar(256), + marked_forwarded boolean, lead_motion_id integer, sort_parent_id integer, origin_id integer, @@ -704,6 +708,7 @@ CREATE TABLE motion_t ( comment on column motion_t.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_t.marked_forwarded is 'Forwarded amendments can be marked as such. This is just optional, however. Forwarded amendments can also have this field set to false.'; CREATE TABLE motion_submitter_t ( @@ -827,6 +832,7 @@ CREATE TABLE motion_state_t ( show_recommendation_extension_field boolean DEFAULT False, merge_amendment_into_final varchar(256) CONSTRAINT enum_motion_state_merge_amendment_into_final CHECK (merge_amendment_into_final IN ('do_not_merge', 'undefined', 'do_merge')) DEFAULT 'undefined', allow_motion_forwarding boolean DEFAULT False, + allow_amendment_forwarding boolean, set_workflow_timestamp boolean DEFAULT False, submitter_withdraw_state_id integer, workflow_id integer NOT NULL, @@ -1168,160 +1174,166 @@ CREATE TABLE import_preview_t ( -- Intermediate table definitions CREATE TABLE nm_meeting_user_supported_motion_ids_motion ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, - motion_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, motion_id) ); CREATE TABLE nm_meeting_user_structure_level_ids_structure_level ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, - structure_level_id integer NOT NULL REFERENCES structure_level_t (id) INITIALLY DEFERRED, + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, structure_level_id) ); CREATE TABLE gm_organization_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) INITIALLY DEFERRED, + organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, - tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id) INITIALLY DEFERRED, - tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id) INITIALLY DEFERRED, + tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id) ON DELETE CASCADE INITIALLY DEFERRED, CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); CREATE TABLE nm_committee_user_ids_user ( - committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, + committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); CREATE TABLE nm_committee_manager_ids_user ( - committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, + committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); +CREATE TABLE nm_committee_all_child_ids_committee ( + all_child_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + all_parent_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (all_child_id, all_parent_id) +); + CREATE TABLE nm_committee_forward_to_committee_ids_committee ( - forward_to_committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, - receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id) INITIALLY DEFERRED, + forward_to_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); CREATE TABLE nm_meeting_present_user_ids_user ( - meeting_id integer NOT NULL REFERENCES meeting_t (id) INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, + meeting_id integer NOT NULL REFERENCES meeting_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_id, user_id) ); CREATE TABLE nm_group_meeting_user_ids_meeting_user ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_user_id) ); CREATE TABLE nm_group_mmagi_meeting_mediafile ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); CREATE TABLE nm_group_mmiagi_meeting_mediafile ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); CREATE TABLE nm_group_poll_ids_poll ( - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, - poll_id integer NOT NULL REFERENCES poll_t (id) INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, poll_id) ); CREATE TABLE gm_tag_tagged_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - tag_id integer NOT NULL REFERENCES tag_t(id) INITIALLY DEFERRED, + tag_id integer NOT NULL REFERENCES tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, - tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id) INITIALLY DEFERRED, - tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) INITIALLY DEFERRED, - tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, + tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); CREATE TABLE nm_motion_all_derived_motion_ids_motion ( - all_derived_motion_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, - all_origin_id integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, + all_derived_motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + all_origin_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (all_derived_motion_id, all_origin_id) ); CREATE TABLE nm_motion_identical_motion_ids_motion ( - identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, - identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id) INITIALLY DEFERRED, + identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); CREATE TABLE gm_motion_state_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motion_t(id) INITIALLY DEFERRED, + motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, state_extension_reference_id varchar(100) NOT NULL, - state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, + state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); CREATE TABLE gm_motion_recommendation_extension_reference_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - motion_id integer NOT NULL REFERENCES motion_t(id) INITIALLY DEFERRED, + motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, recommendation_extension_reference_id varchar(100) NOT NULL, - recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, + recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); CREATE TABLE nm_motion_state_next_state_ids_motion_state ( - next_state_id integer NOT NULL REFERENCES motion_state_t (id) INITIALLY DEFERRED, - previous_state_id integer NOT NULL REFERENCES motion_state_t (id) INITIALLY DEFERRED, + next_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + previous_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (next_state_id, previous_state_id) ); CREATE TABLE nm_poll_voted_ids_user ( - poll_id integer NOT NULL REFERENCES poll_t (id) INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) INITIALLY DEFERRED, + poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (poll_id, user_id) ); CREATE TABLE gm_meeting_mediafile_attachment_ids ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED, + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, attachment_id varchar(100) NOT NULL, - attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) INITIALLY DEFERRED, - attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id) INITIALLY DEFERRED, - attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) INITIALLY DEFERRED, + attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); CREATE TABLE nm_chat_group_read_group_ids_group ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id) INITIALLY DEFERRED, - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); CREATE TABLE nm_chat_group_write_group_ids_group ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id) INITIALLY DEFERRED, - group_id integer NOT NULL REFERENCES group_t (id) INITIALLY DEFERRED, + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); @@ -1390,6 +1402,10 @@ CREATE VIEW "committee" AS SELECT *, (select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, (select array_agg(n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, (select array_agg(n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, +(select array_agg(ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, +(select array_agg(n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, +(select array_agg(n.all_child_id) from nm_committee_all_child_ids_committee n where n.all_parent_id = c.id) as all_child_ids, +(select array_agg(u.id) from user_t u where u.home_committee_id = c.id) as native_user_ids, (select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, (select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, (select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids @@ -1724,12 +1740,14 @@ CREATE VIEW "import_preview" AS SELECT * FROM import_preview_t i; ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; ALTER TABLE user_t ADD FOREIGN KEY(gender_id) REFERENCES gender_t(id) INITIALLY DEFERRED; +ALTER TABLE user_t ADD FOREIGN KEY(home_committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE committee_t ADD FOREIGN KEY(parent_id) REFERENCES committee_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; @@ -2315,6 +2333,7 @@ SQL nt:1Gr => user/option_ids:-> option/content_object_id SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id +FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids @@ -2341,6 +2360,11 @@ SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id SQL nt:nt => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids +FIELD 1r:nt => committee/parent_id:-> committee/child_ids +SQL nt:1r => committee/child_ids:-> committee/parent_id +SQL nt:nt => committee/all_parent_ids:-> committee/all_child_ids +SQL nt:nt => committee/all_child_ids:-> committee/all_parent_ids +SQL nt:1r => committee/native_user_ids:-> user/home_committee_id SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings_from_committee_ids SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids diff --git a/dev/src/db_utils.py b/dev/src/db_utils.py index 01de5ad7..0a256939 100644 --- a/dev/src/db_utils.py +++ b/dev/src/db_utils.py @@ -9,16 +9,14 @@ def get_columns_and_values_for_insert( cls, table: Table, data_list: list[dict[str, Any]], - ) -> tuple[list[Column], list[list[dict[str, Any]]]]: + ) -> tuple[list[Column], list[list[Any | None]]]: """ takes a list of dicts, each one to be inserted Takes care of columns and row positions and fills not existent columns in row with "None" """ - columns: list[Column] = [] - values: list[list[dict[str, Any]]] = [] if not data_list: - return columns, values + return [], [] # use all keys in same sequence keys_set: set = set() for data in data_list: diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 13a8c381..c5e153c2 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -712,8 +712,8 @@ class Helper: dedent( """ CREATE TABLE ${table_name} ( - ${field1} integer NOT NULL REFERENCES ${table1} (id) INITIALLY DEFERRED, - ${field2} integer NOT NULL REFERENCES ${table2} (id) INITIALLY DEFERRED, + ${field1} integer NOT NULL REFERENCES ${table1} (id) ON DELETE CASCADE INITIALLY DEFERRED, + ${field2} integer NOT NULL REFERENCES ${table2} (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (${list_of_keys}) ); """ @@ -724,7 +724,7 @@ class Helper: """ CREATE TABLE ${table_name} ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}) INITIALLY DEFERRED, + ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}) ON DELETE CASCADE INITIALLY DEFERRED, ${own_table_column} varchar(100) NOT NULL, ${foreign_table_ref_lines} CONSTRAINT ${valid_constraint_name} CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), @@ -734,7 +734,7 @@ class Helper: ) ) GM_FOREIGN_TABLE_LINE_TEMPLATE = string.Template( - " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_view_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}(id) INITIALLY DEFERRED," + " ${gm_content_field} integer GENERATED ALWAYS AS (CASE WHEN split_part(${own_table_column}, '/', 1) = '${foreign_view_name}' THEN cast(split_part(${own_table_column}, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES ${foreign_table_name}(id) ON DELETE CASCADE INITIALLY DEFERRED," ) RELATION_LIST_AGENDA = dedent( diff --git a/models.yml b/models.yml index b09d3405..496f6235 100644 --- a/models.yml +++ b/models.yml @@ -366,6 +366,9 @@ user: type: timestamp restriction_mode: A read_only: true + guest: + type: boolean + restriction_mode: E gender_id: type: relation to: gender/user_ids @@ -437,6 +440,11 @@ user: type: relation-list to: poll_candidate/user_id restriction_mode: A + home_committee_id: + type: relation + to: committee/native_user_ids + restriction_mode: E + reference: committee meeting_ids: type: number[] @@ -799,6 +807,27 @@ committee: to: user/committee_management_ids restriction_mode: B + parent_id: + type: relation + to: committee/child_ids + restriction_mode: A + reference: committee + child_ids: + type: relation-list + to: committee/parent_id + restriction_mode: A + all_parent_ids: # Calculated: All parents, grandparents, etc. of this motion. + type: relation-list + to: committee/all_child_ids + restriction_mode: A + all_child_ids: # Calculated: All children, grandchildren, etc. of this motion. + type: relation-list + to: committee/all_parent_ids + restriction_mode: A + native_user_ids: + type: relation-list + to: user/home_committee_id + restriction_mode: B forward_to_committee_ids: type: relation-list to: committee/receive_forwardings_from_committee_ids @@ -2639,6 +2668,10 @@ motion: additional_submitter: type: string restriction_mode: C + marked_forwarded: + type: boolean + restriction_mode: A + description: Forwarded amendments can be marked as such. This is just optional, however. Forwarded amendments can also have this field set to false. lead_motion_id: type: relation @@ -3213,6 +3246,9 @@ motion_state: type: boolean restriction_mode: A default: false + allow_amendment_forwarding: + type: boolean + restriction_mode: A set_workflow_timestamp: type: boolean restriction_mode: A From 5cbb36187ba194d0a4a49b267c0590631dd4233c Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 11 Jun 2025 14:17:11 +0200 Subject: [PATCH 086/142] Unique keyword for 1:1 relations (#272) --- dev/Dockerfile | 2 +- dev/sql/schema_relational.sql | 90 +++++++++++++++++----------------- dev/src/generate_sql_schema.py | 60 +++++++++++++++++------ dev/src/helper_get_names.py | 16 +++++- 4 files changed, 106 insertions(+), 62 deletions(-) diff --git a/dev/Dockerfile b/dev/Dockerfile index 7393dc12..ec632ec8 100644 --- a/dev/Dockerfile +++ b/dev/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10.13-slim-bookworm +FROM python:3.12.9-slim-bookworm RUN apt-get update && apt-get install --yes make bash-completion postgresql-client diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 7f4adcf9..5959a2cb 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -137,7 +137,7 @@ CREATE TABLE organization_t ( saml_metadata_idp text, saml_metadata_sp text, saml_private_key text, - theme_id integer NOT NULL, + theme_id integer NOT NULL UNIQUE, users_email_sender varchar(256) DEFAULT 'OpenSlides', users_email_replyto varchar(256), users_email_subject varchar(256) DEFAULT 'OpenSlides access data', @@ -296,7 +296,7 @@ CREATE TABLE committee_t ( name varchar(256) NOT NULL, description text, external_id varchar(256), - default_meeting_id integer, + default_meeting_id integer UNIQUE, parent_id integer, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -382,8 +382,8 @@ CREATE TABLE meeting_t ( list_of_speakers_default_structure_level_time integer CONSTRAINT minimum_list_of_speakers_default_structure_level_time CHECK (list_of_speakers_default_structure_level_time >= 0), list_of_speakers_enable_interposed_question boolean, list_of_speakers_intervention_time integer, - motions_default_workflow_id integer NOT NULL, - motions_default_amendment_workflow_id integer NOT NULL, + motions_default_workflow_id integer NOT NULL UNIQUE, + motions_default_amendment_workflow_id integer NOT NULL UNIQUE, motions_preamble text DEFAULT 'The assembly may decide:', motions_default_line_numbering varchar(256) CONSTRAINT enum_meeting_motions_default_line_numbering CHECK (motions_default_line_numbering IN ('outside', 'inline', 'none')) DEFAULT 'outside', motions_line_length integer CONSTRAINT minimum_motions_line_length CHECK (motions_line_length >= 40) DEFAULT 85, @@ -471,30 +471,30 @@ This email was generated automatically.', poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', poll_default_backend varchar(256) CONSTRAINT enum_meeting_poll_default_backend CHECK (poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', poll_couple_countdown boolean DEFAULT True, - logo_projector_main_id integer, - logo_projector_header_id integer, - logo_web_header_id integer, - logo_pdf_header_l_id integer, - logo_pdf_header_r_id integer, - logo_pdf_footer_l_id integer, - logo_pdf_footer_r_id integer, - logo_pdf_ballot_paper_id integer, - font_regular_id integer, - font_italic_id integer, - font_bold_id integer, - font_bold_italic_id integer, - font_monospace_id integer, - font_chyron_speaker_name_id integer, - font_projector_h1_id integer, - font_projector_h2_id integer, + logo_projector_main_id integer UNIQUE, + logo_projector_header_id integer UNIQUE, + logo_web_header_id integer UNIQUE, + logo_pdf_header_l_id integer UNIQUE, + logo_pdf_header_r_id integer UNIQUE, + logo_pdf_footer_l_id integer UNIQUE, + logo_pdf_footer_r_id integer UNIQUE, + logo_pdf_ballot_paper_id integer UNIQUE, + font_regular_id integer UNIQUE, + font_italic_id integer UNIQUE, + font_bold_id integer UNIQUE, + font_bold_italic_id integer UNIQUE, + font_monospace_id integer UNIQUE, + font_chyron_speaker_name_id integer UNIQUE, + font_projector_h1_id integer UNIQUE, + font_projector_h2_id integer UNIQUE, committee_id integer NOT NULL, user_ids integer[], - reference_projector_id integer NOT NULL, - list_of_speakers_countdown_id integer, - poll_countdown_id integer, - default_group_id integer NOT NULL, - admin_group_id integer, - anonymous_group_id integer + reference_projector_id integer NOT NULL UNIQUE, + list_of_speakers_countdown_id integer UNIQUE, + poll_countdown_id integer UNIQUE, + default_group_id integer NOT NULL UNIQUE, + admin_group_id integer UNIQUE, + anonymous_group_id integer UNIQUE ); @@ -571,10 +571,10 @@ CREATE TABLE agenda_item_t ( level integer, weight integer, content_object_id varchar(100) NOT NULL, - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic')), parent_id integer, meeting_id integer NOT NULL @@ -594,11 +594,11 @@ CREATE TABLE list_of_speakers_t ( sequential_number integer NOT NULL, moderator_notes text, content_object_id varchar(100) NOT NULL, - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_meeting_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_meeting_mediafile_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','meeting_mediafile')), meeting_id integer NOT NULL ); @@ -846,7 +846,7 @@ CREATE TABLE motion_workflow_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, sequential_number integer NOT NULL, - first_state_id integer NOT NULL, + first_state_id integer NOT NULL UNIQUE, meeting_id integer NOT NULL ); @@ -885,7 +885,7 @@ CREATE TABLE poll_t ( content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), - global_option_id integer, + global_option_id integer UNIQUE, meeting_id integer NOT NULL ); @@ -915,7 +915,7 @@ CREATE TABLE option_t ( content_object_id varchar(100), content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_poll_candidate_list_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_candidate_list_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), meeting_id integer NOT NULL ); @@ -2480,20 +2480,20 @@ FIELD 1r:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids FIELD 1rR:nt => group/meeting_id:-> meeting/group_ids FIELD 1rR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids -FIELD 1Gr: => personal_note/content_object_id:-> motion/ +FIELD 1Gr:nt => personal_note/content_object_id:-> motion/personal_note_ids FIELD 1rR:nt => personal_note/meeting_id:-> meeting/personal_note_ids SQL nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids FIELD 1rR:nt => tag/meeting_id:-> meeting/tag_ids -FIELD 1GrR:,,, => agenda_item/content_object_id:-> motion/,motion_block/,assignment/,topic/ +FIELD 1GrR:1t,1t,1t,1tR => agenda_item/content_object_id:-> motion/agenda_item_id,motion_block/agenda_item_id,assignment/agenda_item_id,topic/agenda_item_id FIELD 1r:nt => agenda_item/parent_id:-> agenda_item/child_ids SQL nt:1r => agenda_item/child_ids:-> agenda_item/parent_id SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id FIELD 1rR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids -FIELD 1GrR:,,,, => list_of_speakers/content_object_id:-> motion/,motion_block/,assignment/,topic/,meeting_mediafile/ +FIELD 1GrR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,meeting_mediafile/list_of_speakers_id SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id @@ -2605,7 +2605,7 @@ SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_def SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids -FIELD 1GrR:,, => poll/content_object_id:-> motion/,assignment/,topic/ +FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids SQL nt:1r => poll/option_ids:-> option/poll_id FIELD 1r:1t => poll/global_option_id:-> option/used_as_global_option_in_poll_id SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids @@ -2616,7 +2616,7 @@ FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids FIELD 1r:nt => option/poll_id:-> poll/option_ids SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id SQL nt:1rR => option/vote_ids:-> vote/option_id -FIELD 1Gr:,, => option/content_object_id:-> motion/,user/,poll_candidate_list/ +FIELD 1Gr:nt,nt,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id FIELD 1rR:nt => option/meeting_id:-> meeting/option_ids FIELD 1rR:nt => vote/option_id:-> option/vote_ids @@ -2648,7 +2648,7 @@ FIELD 1rR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id -FIELD 1GrR:, => mediafile/owner_id:-> meeting/,organization/ +FIELD 1GrR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids SQL nt:1rR => mediafile/meeting_mediafile_ids:-> meeting_mediafile/mediafile_id FIELD 1rR:nt => meeting_mediafile/mediafile_id:-> mediafile/meeting_mediafile_ids @@ -2698,7 +2698,7 @@ FIELD 1rR:nt => projector/meeting_id:-> meeting/projector_ids FIELD 1r:nt => projection/current_projector_id:-> projector/current_projection_ids FIELD 1r:nt => projection/preview_projector_id:-> projector/preview_projection_ids FIELD 1r:nt => projection/history_projector_id:-> projector/history_projection_ids -FIELD 1GrR:,,,,,,,,,, => projection/content_object_id:-> meeting/,motion/,meeting_mediafile/,list_of_speakers/,motion_block/,assignment/,agenda_item/,topic/,poll/,projector_message/,projector_countdown/ +FIELD 1GrR:nt,nt,nt,nt,nt,nt,nt,nt,nt,nt,nt => projection/content_object_id:-> meeting/projection_ids,motion/projection_ids,meeting_mediafile/projection_ids,list_of_speakers/projection_ids,motion_block/projection_ids,assignment/projection_ids,agenda_item/projection_ids,topic/projection_ids,poll/projection_ids,projector_message/projection_ids,projector_countdown/projection_ids FIELD 1rR:nt => projection/meeting_id:-> meeting/all_projection_ids SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index c5e153c2..befa09bf 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -57,6 +57,7 @@ class SubstDict(TypedDict, total=False): minLength: str deferred: str check_enum: str + unique: str class GenerateCodeBlocks: @@ -222,6 +223,23 @@ def get_method( def get_schema_simple_types( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str ) -> tuple[SchemaZoneTexts, str]: + text, subst = cls.get_text_for_simple_types(table_name, fname, fdata, type_) + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text, "" + + @classmethod + def get_schema_relation_1_1( + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str + ) -> tuple[SchemaZoneTexts, str]: + text, subst = cls.get_text_for_simple_types(table_name, fname, fdata, type_) + subst["unique"] = " UNIQUE" + text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + return text, "" + + @classmethod + def get_text_for_simple_types( + cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str + ) -> tuple[SchemaZoneTexts, SubstDict]: text = cast(SchemaZoneTexts, defaultdict(str)) subst, szt = Helper.get_initials(table_name, fname, type_, fdata) text.update(szt) @@ -233,8 +251,7 @@ def get_schema_simple_types( elif isinstance(type_, str): # string tmp = tmp.substitute({"maxLength": 256}) subst["type"] = tmp - text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) - return text, "" + return text, subst @classmethod def get_schema_color( @@ -285,19 +302,24 @@ def get_relation_type( own_table_field, [foreign_table_field] ) + foreign_table = foreign_table_field.table if state == FieldSqlErrorType.FIELD: - text, error = cls.get_schema_simple_types( - table_name, fname, fdata, "number" - ) + foreign_card, error = InternalHelper.get_cardinality(foreign_table_field) + if foreign_card.startswith("1"): + text, error = cls.get_schema_relation_1_1( + table_name, fname, fdata, "number" + ) + else: + text, error = cls.get_schema_simple_types( + table_name, fname, fdata, "number" + ) initially_deferred = fdata.get( "deferred" - ) or ModelsHelper.is_fk_initially_deferred( - table_name, foreign_table_field.table - ) + ) or ModelsHelper.is_fk_initially_deferred(table_name, foreign_table) text["alter_table_final"] = ( Helper.get_foreign_key_table_constraint_as_alter_table( table_name, - foreign_table_field.table, + foreign_table, fname, foreign_table_field.ref_column, initially_deferred, @@ -311,7 +333,7 @@ def get_relation_type( table_name, fname, foreign_table_field.ref_column, - foreign_table_field.table, + foreign_table, f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}", ) else: @@ -319,7 +341,7 @@ def get_relation_type( table_name, fname, foreign_table_field.ref_column, - foreign_table_field.table, + foreign_table, cast(str, foreign_table_field.column), ) text["final_info"] = final_info @@ -528,7 +550,7 @@ def get_generic_relation_type( text["table"] += Helper.get_generic_combined_fields( generic_plain_field_name, own_table_field.column, - foreign_table_field.table, + foreign_table_field, ) text[ "alter_table_final" @@ -706,7 +728,7 @@ class Helper: """ ) FIELD_TEMPLATE = string.Template( - " ${field_name} ${type}${primary_key}${required}${check_enum}${minimum}${minLength}${default},\n" + " ${field_name} ${type}${primary_key}${required}${unique}${check_enum}${minimum}${minLength}${default},\n" ) INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE = string.Template( dedent( @@ -1024,9 +1046,17 @@ def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: @staticmethod def get_generic_combined_fields( - generic_plain_field_name: str, own_column: str, foreign_table: str + generic_plain_field_name: str, own_column: str, foreign_field: TableFieldType ) -> str: - return f" {generic_plain_field_name} integer GENERATED ALWAYS AS (CASE WHEN split_part({own_column}, '/', 1) = '{foreign_table}' THEN cast(split_part({own_column}, '/', 2) AS INTEGER) ELSE null END) STORED,\n" + foreign_table = foreign_field.table + foreign_card, error = InternalHelper.get_cardinality(foreign_field) + if error: + raise Exception(error) + if foreign_card.startswith("1"): + unique = " UNIQUE" + else: + unique = "" + return f" {generic_plain_field_name} integer{unique} GENERATED ALWAYS AS (CASE WHEN split_part({own_column}, '/', 1) = '{foreign_table}' THEN cast(split_part({own_column}, '/', 2) AS INTEGER) ELSE null END) STORED,\n" @staticmethod def get_generic_field_constraint(own_column: str, foreign_tables: list[str]) -> str: diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 763ad9ff..05214ca4 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -480,7 +480,21 @@ def get_definitions_from_foreign_list( # precedence for reference if reference: for ref in reference: - results.append(TableFieldType.get_definitions_from_foreign(None, ref)) + if isinstance(to, dict): + to_field = f"{ref}/{to['field']}" + elif isinstance(to, list): + for collectionfield in to: + if collectionfield.startswith(ref): + to_field = collectionfield + elif isinstance(to, str): + to_field = to + else: + raise Exception("No valid type for 'to:' provided.") + if not to_field: + raise Exception("Couldn't find foreign field.") + results.append( + TableFieldType.get_definitions_from_foreign(to_field, ref) + ) elif isinstance(to, dict): fname = "/" + to["field"] for table in to["collections"]: From ced8eb5f2cd72627b9a536d57a45b29b58cb8d4a Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Fri, 13 Jun 2025 10:50:31 +0200 Subject: [PATCH 087/142] give ordered output on view relation lists (#273) --- dev/sql/schema_relational.sql | 366 ++++++++++++++++----------------- dev/src/generate_sql_schema.py | 14 +- 2 files changed, 193 insertions(+), 187 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 5959a2cb..933c60e8 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1341,55 +1341,55 @@ CREATE TABLE nm_chat_group_write_group_ids_group ( -- View definitions CREATE VIEW "organization" AS SELECT *, -(select array_agg(g.id) from gender_t g where g.organization_id = o.id) as gender_ids, -(select array_agg(c.id) from committee_t c where c.organization_id = o.id) as committee_ids, -(select array_agg(m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, -(select array_agg(m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, -(select array_agg(m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, -(select array_agg(t.id) from theme_t t where t.organization_id = o.id) as theme_ids, -(select array_agg(m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, -(select array_agg(m.id) from mediafile_t m where m.published_to_meetings_in_organization_id = o.id) as published_mediafile_ids, -(select array_agg(u.id) from user_t u where u.organization_id = o.id) as user_ids +(select array_agg(g.id ORDER BY g.id) from gender_t g where g.organization_id = o.id) as gender_ids, +(select array_agg(c.id ORDER BY c.id) from committee_t c where c.organization_id = o.id) as committee_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, +(select array_agg(ot.id ORDER BY ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, +(select array_agg(t.id ORDER BY t.id) from theme_t t where t.organization_id = o.id) as theme_ids, +(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.published_to_meetings_in_organization_id = o.id) as published_mediafile_ids, +(select array_agg(u.id ORDER BY u.id) from user_t u where u.organization_id = o.id) as user_ids FROM organization_t o; CREATE VIEW "user" AS SELECT *, -(select array_agg(n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, -(select array_agg(n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, -(select array_agg(m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, -(select array_agg(v.id) from vote_t v where v.user_id = u.id) as vote_ids, -(select array_agg(v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, -(select array_agg(p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids +(select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, +(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids FROM user_t u; comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; CREATE VIEW "meeting_user" AS SELECT *, -(select array_agg(p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, -(select array_agg(s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(n.motion_id) from nm_meeting_user_supported_motion_ids_motion n where n.meeting_user_id = m.id) as supported_motion_ids, -(select array_agg(me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, -(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, -(select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, -(select array_agg(a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, -(select array_agg(mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, -(select array_agg(c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_group_meeting_user_ids_meeting_user n where n.meeting_user_id = m.id) as group_ids, -(select array_agg(n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level n where n.meeting_user_id = m.id) as structure_level_ids +(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, +(select array_agg(n.motion_id ORDER BY n.motion_id) from nm_meeting_user_supported_motion_ids_motion n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, +(select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, +(select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, +(select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, +(select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level n where n.meeting_user_id = m.id) as structure_level_ids FROM meeting_user_t m; CREATE VIEW "gender" AS SELECT *, -(select array_agg(u.id) from user_t u where u.gender_id = g.id) as user_ids +(select array_agg(u.id ORDER BY u.id) from user_t u where u.gender_id = g.id) as user_ids FROM gender_t g; CREATE VIEW "organization_tag" AS SELECT *, -(select array_agg(g.tagged_id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; @@ -1399,102 +1399,102 @@ FROM theme_t t; CREATE VIEW "committee" AS SELECT *, -(select array_agg(m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -(select array_agg(n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, -(select array_agg(n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, -(select array_agg(ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, -(select array_agg(n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, -(select array_agg(n.all_child_id) from nm_committee_all_child_ids_committee n where n.all_parent_id = c.id) as all_child_ids, -(select array_agg(u.id) from user_t u where u.home_committee_id = c.id) as native_user_ids, -(select array_agg(n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, -(select array_agg(n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, +(select array_agg(ct.id ORDER BY ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, +(select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, +(select array_agg(n.all_child_id ORDER BY n.all_child_id) from nm_committee_all_child_ids_committee n where n.all_parent_id = c.id) as all_child_ids, +(select array_agg(u.id ORDER BY u.id) from user_t u where u.home_committee_id = c.id) as native_user_ids, +(select array_agg(n.forward_to_committee_id ORDER BY n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, +(select array_agg(n.receive_forwardings_from_committee_id ORDER BY n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, +(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; comment on column "committee".user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; CREATE VIEW "meeting" AS SELECT *, -(select array_agg(g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, -(select array_agg(p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, -(select array_agg(p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, -(select array_agg(mu.id) from meeting_user_t mu where mu.meeting_id = m.id) as meeting_user_ids, -(select array_agg(g.id) from group_t g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, -(select array_agg(g.id) from group_t g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, -(select array_agg(g.id) from group_t g where g.used_as_topic_poll_default_id = m.id) as topic_poll_default_group_ids, -(select array_agg(p.id) from projector_t p where p.meeting_id = m.id) as projector_ids, -(select array_agg(p.id) from projection_t p where p.meeting_id = m.id) as all_projection_ids, -(select array_agg(p.id) from projector_message_t p where p.meeting_id = m.id) as projector_message_ids, -(select array_agg(p.id) from projector_countdown_t p where p.meeting_id = m.id) as projector_countdown_ids, -(select array_agg(t.id) from tag_t t where t.meeting_id = m.id) as tag_ids, -(select array_agg(a.id) from agenda_item_t a where a.meeting_id = m.id) as agenda_item_ids, -(select array_agg(l.id) from list_of_speakers_t l where l.meeting_id = m.id) as list_of_speakers_ids, -(select array_agg(s.id) from structure_level_list_of_speakers_t s where s.meeting_id = m.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.id) from point_of_order_category_t p where p.meeting_id = m.id) as point_of_order_category_ids, -(select array_agg(s.id) from speaker_t s where s.meeting_id = m.id) as speaker_ids, -(select array_agg(t.id) from topic_t t where t.meeting_id = m.id) as topic_ids, -(select array_agg(g.id) from group_t g where g.meeting_id = m.id) as group_ids, -(select array_agg(mm.id) from meeting_mediafile_t mm where mm.meeting_id = m.id) as meeting_mediafile_ids, -(select array_agg(mt.id) from mediafile_t mt where mt.owner_id_meeting_id = m.id) as mediafile_ids, -(select array_agg(mt.id) from motion_t mt where mt.meeting_id = m.id) as motion_ids, -(select array_agg(mt.id) from motion_t mt where mt.origin_meeting_id = m.id) as forwarded_motion_ids, -(select array_agg(mc.id) from motion_comment_section_t mc where mc.meeting_id = m.id) as motion_comment_section_ids, -(select array_agg(mc.id) from motion_category_t mc where mc.meeting_id = m.id) as motion_category_ids, -(select array_agg(mb.id) from motion_block_t mb where mb.meeting_id = m.id) as motion_block_ids, -(select array_agg(mw.id) from motion_workflow_t mw where mw.meeting_id = m.id) as motion_workflow_ids, -(select array_agg(mc.id) from motion_comment_t mc where mc.meeting_id = m.id) as motion_comment_ids, -(select array_agg(ms.id) from motion_submitter_t ms where ms.meeting_id = m.id) as motion_submitter_ids, -(select array_agg(me.id) from motion_editor_t me where me.meeting_id = m.id) as motion_editor_ids, -(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, -(select array_agg(mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, -(select array_agg(ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, -(select array_agg(p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, -(select array_agg(o.id) from option_t o where o.meeting_id = m.id) as option_ids, -(select array_agg(v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, -(select array_agg(a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, -(select array_agg(a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, -(select array_agg(p.id) from personal_note_t p where p.meeting_id = m.id) as personal_note_ids, -(select array_agg(c.id) from chat_group_t c where c.meeting_id = m.id) as chat_group_ids, -(select array_agg(c.id) from chat_message_t c where c.meeting_id = m.id) as chat_message_ids, -(select array_agg(s.id) from structure_level_t s where s.meeting_id = m.id) as structure_level_ids, +(select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, +(select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.meeting_id = m.id) as meeting_user_ids, +(select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, +(select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, +(select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_topic_poll_default_id = m.id) as topic_poll_default_group_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.meeting_id = m.id) as projector_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.meeting_id = m.id) as all_projection_ids, +(select array_agg(p.id ORDER BY p.id) from projector_message_t p where p.meeting_id = m.id) as projector_message_ids, +(select array_agg(p.id ORDER BY p.id) from projector_countdown_t p where p.meeting_id = m.id) as projector_countdown_ids, +(select array_agg(t.id ORDER BY t.id) from tag_t t where t.meeting_id = m.id) as tag_ids, +(select array_agg(a.id ORDER BY a.id) from agenda_item_t a where a.meeting_id = m.id) as agenda_item_ids, +(select array_agg(l.id ORDER BY l.id) from list_of_speakers_t l where l.meeting_id = m.id) as list_of_speakers_ids, +(select array_agg(s.id ORDER BY s.id) from structure_level_list_of_speakers_t s where s.meeting_id = m.id) as structure_level_list_of_speakers_ids, +(select array_agg(p.id ORDER BY p.id) from point_of_order_category_t p where p.meeting_id = m.id) as point_of_order_category_ids, +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_id = m.id) as speaker_ids, +(select array_agg(t.id ORDER BY t.id) from topic_t t where t.meeting_id = m.id) as topic_ids, +(select array_agg(g.id ORDER BY g.id) from group_t g where g.meeting_id = m.id) as group_ids, +(select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.meeting_id = m.id) as meeting_mediafile_ids, +(select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.owner_id_meeting_id = m.id) as mediafile_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.meeting_id = m.id) as motion_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.origin_meeting_id = m.id) as forwarded_motion_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_comment_section_t mc where mc.meeting_id = m.id) as motion_comment_section_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_category_t mc where mc.meeting_id = m.id) as motion_category_ids, +(select array_agg(mb.id ORDER BY mb.id) from motion_block_t mb where mb.meeting_id = m.id) as motion_block_ids, +(select array_agg(mw.id ORDER BY mw.id) from motion_workflow_t mw where mw.meeting_id = m.id) as motion_workflow_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.meeting_id = m.id) as motion_comment_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_id = m.id) as motion_submitter_ids, +(select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_id = m.id) as motion_editor_ids, +(select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.meeting_id = m.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, +(select array_agg(a.id ORDER BY a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, +(select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, +(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_id = m.id) as personal_note_ids, +(select array_agg(c.id ORDER BY c.id) from chat_group_t c where c.meeting_id = m.id) as chat_group_ids, +(select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_id = m.id) as chat_message_ids, +(select array_agg(s.id ORDER BY s.id) from structure_level_t s where s.meeting_id = m.id) as structure_level_ids, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, -(select array_agg(g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, -(select array_agg(n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_los_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_assignment_in_meeting_id = m.id) as default_projector_assignment_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_mediafile_in_meeting_id = m.id) as default_projector_mediafile_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_message_in_meeting_id = m.id) as default_projector_message_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, -(select array_agg(p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids +(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_list_of_speakers_in_meeting_id = m.id) as default_projector_list_of_speakers_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_current_los_in_meeting_id = m.id) as default_projector_current_los_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_motion_in_meeting_id = m.id) as default_projector_motion_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_amendment_in_meeting_id = m.id) as default_projector_amendment_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_motion_block_in_meeting_id = m.id) as default_projector_motion_block_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_assignment_in_meeting_id = m.id) as default_projector_assignment_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_mediafile_in_meeting_id = m.id) as default_projector_mediafile_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_message_in_meeting_id = m.id) as default_projector_message_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids FROM meeting_t m; CREATE VIEW "structure_level" AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, -(select array_agg(sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(sl.id ORDER BY sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids FROM structure_level_t s; CREATE VIEW "group" AS SELECT *, -(select array_agg(n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user n where n.group_id = g.id) as meeting_user_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, -(select array_agg(n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_access_group_ids, -(select array_agg(n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.group_id = g.id) as read_comment_section_ids, -(select array_agg(n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.group_id = g.id) as write_comment_section_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_read_group_ids_group n where n.group_id = g.id) as read_chat_group_ids, -(select array_agg(n.chat_group_id) from nm_chat_group_write_group_ids_group n where n.group_id = g.id) as write_chat_group_ids, -(select array_agg(n.poll_id) from nm_group_poll_ids_poll n where n.group_id = g.id) as poll_ids +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_access_group_ids, +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_read_group_ids_group n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_write_group_ids_group n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_group_poll_ids_poll n where n.group_id = g.id) as poll_ids FROM group_t g; comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; @@ -1503,31 +1503,31 @@ CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; CREATE VIEW "tag" AS SELECT *, -(select array_agg(g.tagged_id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids FROM tag_t t; CREATE VIEW "agenda_item" AS SELECT *, -(select array_agg(ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_agenda_item_id = a.id) as tag_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids +(select array_agg(ai.id ORDER BY ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_item_t a; CREATE VIEW "list_of_speakers" AS SELECT *, -(select array_agg(s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, -(select array_agg(s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, +(select array_agg(s.id ORDER BY s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids FROM list_of_speakers_t l; CREATE VIEW "structure_level_list_of_speakers" AS SELECT *, -(select array_agg(st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids +(select array_agg(st.id ORDER BY st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids FROM structure_level_list_of_speakers_t s; CREATE VIEW "point_of_order_category" AS SELECT *, -(select array_agg(s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids FROM point_of_order_category_t p; @@ -1535,39 +1535,39 @@ CREATE VIEW "speaker" AS SELECT * FROM speaker_t s; CREATE VIEW "topic" AS SELECT *, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, -(select array_agg(p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_topic_id = t.id) as projection_ids +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_topic_id = t.id) as projection_ids FROM topic_t t; CREATE VIEW "motion" AS SELECT *, -(select array_agg(mt.id) from motion_t mt where mt.lead_motion_id = m.id) as amendment_ids, -(select array_agg(mt.id) from motion_t mt where mt.sort_parent_id = m.id) as sort_child_ids, -(select array_agg(mt.id) from motion_t mt where mt.origin_id = m.id) as derived_motion_ids, -(select array_agg(n.all_origin_id) from nm_motion_all_derived_motion_ids_motion n where n.all_derived_motion_id = m.id) as all_origin_ids, -(select array_agg(n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion n where n.all_origin_id = m.id) as all_derived_motion_ids, -(select array_cat((select array_agg(n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, -(select array_agg(g.state_extension_reference_id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_state_extension_reference_ids g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, -(select array_agg(g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, -(select array_agg(g.motion_id) from gm_motion_recommendation_extension_reference_ids g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, -(select array_agg(ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, -(select array_agg(n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion n where n.motion_id = m.id) as supporter_meeting_user_ids, -(select array_agg(me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, -(select array_agg(mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, -(select array_agg(p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, -(select array_agg(o.id) from option_t o where o.content_object_id_motion_id = m.id) as option_ids, -(select array_agg(mc.id) from motion_change_recommendation_t mc where mc.motion_id = m.id) as change_recommendation_ids, -(select array_agg(mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.lead_motion_id = m.id) as amendment_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.sort_parent_id = m.id) as sort_child_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.origin_id = m.id) as derived_motion_ids, +(select array_agg(n.all_origin_id ORDER BY n.all_origin_id) from nm_motion_all_derived_motion_ids_motion n where n.all_derived_motion_id = m.id) as all_origin_ids, +(select array_agg(n.all_derived_motion_id ORDER BY n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_cat((select array_agg(n.identical_motion_id_1 ORDER BY n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2 ORDER BY n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, +(select array_agg(g.state_extension_reference_id ORDER BY g.state_extension_reference_id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_state_extension_reference_ids g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.recommendation_extension_reference_id ORDER BY g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_recommendation_extension_reference_ids g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, +(select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_motion_id = m.id) as option_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.motion_id = m.id) as change_recommendation_ids, +(select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, -(select array_agg(p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, +(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids FROM motion_t m; @@ -1584,23 +1584,23 @@ CREATE VIEW "motion_comment" AS SELECT * FROM motion_comment_t m; CREATE VIEW "motion_comment_section" AS SELECT *, -(select array_agg(mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, -(select array_agg(n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, -(select array_agg(n.group_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as write_group_ids +(select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as write_group_ids FROM motion_comment_section_t m; CREATE VIEW "motion_category" AS SELECT *, -(select array_agg(mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, -(select array_agg(mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids +(select array_agg(mc.id ORDER BY mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids FROM motion_category_t m; CREATE VIEW "motion_block" AS SELECT *, -(select array_agg(mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, -(select array_agg(p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids FROM motion_block_t m; @@ -1608,33 +1608,33 @@ CREATE VIEW "motion_change_recommendation" AS SELECT * FROM motion_change_recomm CREATE VIEW "motion_state" AS SELECT *, -(select array_agg(ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, -(select array_agg(n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, -(select array_agg(n.previous_state_id) from nm_motion_state_next_state_ids_motion_state n where n.next_state_id = m.id) as previous_state_ids, -(select array_agg(mt.id) from motion_t mt where mt.state_id = m.id) as motion_ids, -(select array_agg(mt.id) from motion_t mt where mt.recommendation_id = m.id) as motion_recommendation_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, +(select array_agg(n.next_state_id ORDER BY n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, +(select array_agg(n.previous_state_id ORDER BY n.previous_state_id) from nm_motion_state_next_state_ids_motion_state n where n.next_state_id = m.id) as previous_state_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.state_id = m.id) as motion_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.recommendation_id = m.id) as motion_recommendation_ids, (select mw.id from motion_workflow_t mw where mw.first_state_id = m.id) as first_state_of_workflow_id FROM motion_state_t m; CREATE VIEW "motion_workflow" AS SELECT *, -(select array_agg(ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, (select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, (select m1.id from meeting_t m1 where m1.motions_default_amendment_workflow_id = m.id) as default_amendment_workflow_meeting_id FROM motion_workflow_t m; CREATE VIEW "poll" AS SELECT *, -(select array_agg(o.id) from option_t o where o.poll_id = p.id) as option_ids, -(select array_agg(n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, -(select array_agg(n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, -(select array_agg(pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids +(select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; CREATE VIEW "option" AS SELECT *, (select p.id from poll_t p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, -(select array_agg(v.id) from vote_t v where v.option_id = o.id) as vote_ids +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.option_id = o.id) as vote_ids FROM option_t o; @@ -1642,13 +1642,13 @@ CREATE VIEW "vote" AS SELECT * FROM vote_t v; CREATE VIEW "assignment" AS SELECT *, -(select array_agg(ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, -(select array_agg(p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, +(select array_agg(ac.id ORDER BY ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, -(select array_agg(p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids FROM assignment_t a; @@ -1656,7 +1656,7 @@ CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; CREATE VIEW "poll_candidate_list" AS SELECT *, -(select array_agg(pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, +(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, (select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id FROM poll_candidate_list_t p; @@ -1665,17 +1665,17 @@ CREATE VIEW "poll_candidate" AS SELECT * FROM poll_candidate_t p; CREATE VIEW "mediafile" AS SELECT *, -(select array_agg(mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, -(select array_agg(mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids +(select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, +(select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids FROM mediafile_t m; CREATE VIEW "meeting_mediafile" AS SELECT *, -(select array_agg(n.group_id) from nm_group_mmiagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmiagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, (select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, -(select array_agg(p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, -(select array_agg(g.attachment_id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, +(select array_agg(g.attachment_id ORDER BY g.attachment_id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, (select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, @@ -1697,9 +1697,9 @@ FROM meeting_mediafile_t m; comment on column "meeting_mediafile".inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; CREATE VIEW "projector" AS SELECT *, -(select array_agg(pt.id) from projection_t pt where pt.current_projector_id = p.id) as current_projection_ids, -(select array_agg(pt.id) from projection_t pt where pt.preview_projector_id = p.id) as preview_projection_ids, -(select array_agg(pt.id) from projection_t pt where pt.history_projector_id = p.id) as history_projection_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.current_projector_id = p.id) as current_projection_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.preview_projector_id = p.id) as preview_projection_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.history_projector_id = p.id) as history_projection_ids, (select m.id from meeting_t m where m.reference_projector_id = p.id) as used_as_reference_projector_meeting_id FROM projector_t p; @@ -1708,21 +1708,21 @@ CREATE VIEW "projection" AS SELECT * FROM projection_t p; CREATE VIEW "projector_message" AS SELECT *, -(select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_message_id = p.id) as projection_ids +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_projector_message_id = p.id) as projection_ids FROM projector_message_t p; CREATE VIEW "projector_countdown" AS SELECT *, -(select array_agg(pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, (select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, (select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id FROM projector_countdown_t p; CREATE VIEW "chat_group" AS SELECT *, -(select array_agg(cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, -(select array_agg(n.group_id) from nm_chat_group_read_group_ids_group n where n.chat_group_id = c.id) as read_group_ids, -(select array_agg(n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids +(select array_agg(cm.id ORDER BY cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_read_group_ids_group n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids FROM chat_group_t c; diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index befa09bf..546f9540 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -485,18 +485,24 @@ def get_sql_for_relation_n_1( ) -> str: table_letter = Helper.get_table_letter(table_name) foreign_letter = Helper.get_table_letter(foreign_table_name, [table_letter]) - AGG_TEMPLATE = f"select array_agg({foreign_letter}.{{}}) from {foreign_table_name} {foreign_letter}" + AGG_TEMPLATE = f"select array_agg({foreign_letter}.{{}} ORDER BY {foreign_letter}.{{}}) from {foreign_table_name} {foreign_letter}" COND_TEMPLATE = ( f" where {foreign_letter}.{{}} = {table_letter}.{own_ref_column}" ) if not foreign_table_column or not self_reference: - query = AGG_TEMPLATE.format(foreign_table_ref_column) + query = AGG_TEMPLATE.format( + foreign_table_ref_column, foreign_table_ref_column + ) if foreign_table_column: query += COND_TEMPLATE.format(foreign_table_column) else: assert foreign_table_ref_column == (col := foreign_table_column) - arr1 = AGG_TEMPLATE.format(f"{col}_1") + COND_TEMPLATE.format(f"{col}_2") - arr2 = AGG_TEMPLATE.format(f"{col}_2") + COND_TEMPLATE.format(f"{col}_1") + arr1 = AGG_TEMPLATE.format(f"{col}_1", f"{col}_1") + COND_TEMPLATE.format( + f"{col}_2" + ) + arr2 = AGG_TEMPLATE.format(f"{col}_2", f"{col}_2") + COND_TEMPLATE.format( + f"{col}_1" + ) query = f"select array_cat(({arr1}), ({arr2}))" return f"({query}) as {fname},\n" From 332013b5bb5a5ac3e99065d0c6e35bc2c99ade9b Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 2 Jul 2025 12:18:22 +0200 Subject: [PATCH 088/142] use custom sql for relations of user with committee and meeting (#277) --- dev/sql/schema_relational.sql | 46 +++++++++------ dev/src/generate_sql_schema.py | 14 +++-- models.yml | 101 +++++++++++++++++++++++++-------- 3 files changed, 116 insertions(+), 45 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 933c60e8..aa779774 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '849102a66a0fdb700779571c3d949b36' +-- MODELS_YML_CHECKSUM = '0fb7c194db5c11eca64e4fef8ee1f60d' -- Database parameters @@ -189,7 +189,6 @@ CREATE TABLE user_t ( gender_id integer, organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), home_committee_id integer, - meeting_ids integer[], organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); @@ -197,7 +196,6 @@ CREATE TABLE user_t ( comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; -comment on column user_t.meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; CREATE TABLE meeting_user_t ( @@ -488,7 +486,6 @@ This email was generated automatically.', font_projector_h1_id integer UNIQUE, font_projector_h2_id integer UNIQUE, committee_id integer NOT NULL, - user_ids integer[], reference_projector_id integer NOT NULL UNIQUE, list_of_speakers_countdown_id integer UNIQUE, poll_countdown_id integer UNIQUE, @@ -504,7 +501,6 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meeting_t.user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; CREATE TABLE structure_level_t ( @@ -1195,12 +1191,6 @@ CREATE TABLE gm_organization_tag_tagged_ids ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE nm_committee_user_ids_user ( - committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (committee_id, user_id) -); - CREATE TABLE nm_committee_manager_ids_user ( committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1356,17 +1346,27 @@ FROM organization_t o; CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, -(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_user_ids_user n where n.user_id = u.id) as committee_ids, +( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( +-- Select committee_ids from meetings the user is part of +SELECT m.committee_id FROM meeting_user_t AS mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id INNER JOIN meeting_t AS m ON m.id = mu.meeting_id WHERE mu.user_id = u.id +UNION +-- Select committee_ids from committee managers +SELECT cmu.committee_id FROM nm_committee_manager_ids_user cmu WHERE cmu.user_id = u.id +UNION +-- Select home_committee_id from user +SELECT u.home_committee_id WHERE u.home_committee_id IS NOT NULL ) _ ) AS committee_ids, (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, (select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, (select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, -(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, +( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids FROM user_t u; comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column "user".meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, @@ -1400,7 +1400,15 @@ FROM theme_t t; CREATE VIEW "committee" AS SELECT *, (select array_agg(m.id ORDER BY m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -(select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_user_ids_user n where n.committee_id = c.id) as user_ids, +( SELECT array_agg(DISTINCT user_id ORDER BY user_id) FROM ( +-- Select user_ids from committees meetings +SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id +UNION +-- Select user_ids from committee managers +SELECT cmu.user_id FROM nm_committee_manager_ids_user cmu WHERE cmu.committee_id = c.id +UNION +-- Select user_id from home committees +SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id ) _ ) AS user_ids, (select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, (select array_agg(ct.id ORDER BY ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, (select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, @@ -1459,6 +1467,7 @@ CREATE VIEW "meeting" AS SELECT *, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, +( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, @@ -1476,6 +1485,9 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids FROM meeting_t m; +comment on column "meeting".is_active_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column "meeting".is_archived_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column "meeting".user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; CREATE VIEW "structure_level" AS SELECT *, (select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, @@ -2325,7 +2337,7 @@ SQL nr:1rR => organization/user_ids:-> user/organization_id FIELD 1r:nr => user/gender_id:-> gender/user_ids SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids -SQL nt:nt => user/committee_ids:-> committee/user_ids +SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids @@ -2334,6 +2346,7 @@ SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids +SQL nts:nts => user/meeting_ids:-> meeting/user_ids FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids @@ -2358,7 +2371,7 @@ SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id -SQL nt:nt => committee/user_ids:-> user/committee_ids +SQL nts:nts => committee/user_ids:-> user/committee_ids SQL nt:nt => committee/manager_ids:-> user/committee_management_ids FIELD 1r:nt => committee/parent_id:-> committee/child_ids SQL nt:1r => committee/child_ids:-> committee/parent_id @@ -2436,6 +2449,7 @@ FIELD 1rR:nt => meeting/committee_id:-> committee/meeting_ids SQL 1t:1r => meeting/default_meeting_for_committee_id:-> committee/default_meeting_id SQL nt:nGt => meeting/organization_tag_ids:-> organization_tag/tagged_ids SQL nt:nt => meeting/present_user_ids:-> user/is_present_in_meeting_ids +SQL nts:nts => meeting/user_ids:-> user/meeting_ids FIELD 1rR:1t => meeting/reference_projector_id:-> projector/used_as_reference_projector_meeting_id FIELD 1r:1t => meeting/list_of_speakers_countdown_id:-> projector_countdown/used_as_list_of_speakers_countdown_meeting_id FIELD 1r:1t => meeting/poll_countdown_id:-> projector_countdown/used_as_poll_countdown_meeting_id diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 546f9540..98e245d7 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -102,7 +102,7 @@ def generate_the_code( "to", "reference", # "on_delete", # must have other name then the key-value-store one - # "sql" + "sql", # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? # "unique", # TODO: still to design } @@ -344,6 +344,10 @@ def get_relation_type( foreign_table, cast(str, foreign_table_field.column), ) + if comment := fdata.get("description"): + text["post_view"] += Helper.get_post_view_comment( + HelperGetNames.get_view_name(table_name), fname, comment + ) text["final_info"] = final_info return text, error @@ -456,10 +460,6 @@ def get_relation_list_type( foreign_table_ref_column, own_table_field.field_def == foreign_table_field.field_def, ) - if comment := fdata.get("description"): - text["post_view"] = Helper.get_post_view_comment( - HelperGetNames.get_view_name(table_name), fname, comment - ) if own_table_field.field_def.get("required"): text["create_trigger_relationlistnotnull"] = ( cls.get_trigger_check_not_null_for_relation_lists( @@ -469,6 +469,10 @@ def get_relation_list_type( foreign_table_field.column, ) ) + if comment := fdata.get("description"): + text["post_view"] += Helper.get_post_view_comment( + HelperGetNames.get_view_name(table_name), fname, comment + ) text["final_info"] = final_info return text, error diff --git a/models.yml b/models.yml index 496f6235..2798b781 100644 --- a/models.yml +++ b/models.yml @@ -391,23 +391,43 @@ user: # Calculates all committee's where the user is # - committee-manager (= committees in committee_management_ids) - # - is member (= has group-rights) of a meeting in the committee - # TODO: fix sql, not correct for the current design and naming + # - is member (= is in a group) of a meeting in the committee + # - or has his home committee committee_ids: type: relation-list to: committee/user_ids restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - # sql: >- # TODO - # (select array_agg(committee_id) from - # select m.committee_id as committee_id from group_to_user gtu - # join groupT g on g.id = gtu.group_id - # join meetingT m on m.id = g.meeting_id - # where gtu.user_id = u.id - # union - # select ctu.committee_id as committee_id from committee_manager_to_user ctu where ctu.user_id = u.id - # ) as committee_ids + sql: + ( + SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + FROM ( + + -- Select committee_ids from meetings the user is part of + + SELECT m.committee_id + FROM meeting_user_t AS mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id + + UNION + + -- Select committee_ids from committee managers + + SELECT cmu.committee_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.user_id = u.id + + UNION + + -- Select home_committee_id from user + + SELECT u.home_committee_id + WHERE u.home_committee_id IS NOT NULL + ) _ + ) AS committee_ids # committee specific permissions committee_management_ids: @@ -447,10 +467,17 @@ user: reference: committee meeting_ids: - type: number[] + type: relation-list description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true - # sql: todo + sql: + ( + SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.user_id = u.id + ) AS meeting_ids + to: meeting/user_ids restriction_mode: E organization_id: type: relation @@ -793,15 +820,34 @@ committee: restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - # sql: >- # TODO - # (select array_agg(user_id) from - # select gtu.user_id as user_id from meetingT m - # join groupT g on g.meeting_id = m.id - # join group_to_user gtu on gtu.group_id = g.id - # where m.committee_id = c.id - # union - # select ctu.user_id as user_id from committee_manager_to_user ctu where ctu.committee_id = c.id - # ) as user_ids + sql: + "( + SELECT array_agg(DISTINCT user_id ORDER BY user_id) + FROM ( + + -- Select user_ids from committees meetings + + SELECT mu.user_id + FROM meeting_t AS m + INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE m.committee_id = c.id + + UNION + + -- Select user_ids from committee managers + + SELECT cmu.user_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.committee_id = c.id + + UNION + + -- Select user_id from home committees + + SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id + ) _ + ) AS user_ids" manager_ids: type: relation-list to: user/committee_management_ids @@ -1889,10 +1935,17 @@ meeting: to: user/is_present_in_meeting_ids restriction_mode: B user_ids: - type: number[] + type: relation-list description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true - # sql: todo + sql: + ( + SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.meeting_id = m.id + ) AS user_ids + to: user/meeting_ids restriction_mode: A reference_projector_id: type: relation From 7604835edb8ac659ff602c7edc2cdba940a6e85a Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 17 Jul 2025 07:36:04 +0200 Subject: [PATCH 089/142] Fix and unify the sql attributes --- models.yml | 78 +++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/models.yml b/models.yml index ffff8ede..85539f7f 100644 --- a/models.yml +++ b/models.yml @@ -399,7 +399,7 @@ user: restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sql: + sql: | ( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( @@ -420,7 +420,7 @@ user: FROM nm_committee_manager_ids_user cmu WHERE cmu.user_id = u.id - UNION + UNION -- Select home_committee_id from user @@ -470,12 +470,12 @@ user: type: relation-list description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true - sql: + sql: | ( - SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id - WHERE mu.user_id = u.id + SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.user_id = u.id ) AS meeting_ids to: meeting/user_ids restriction_mode: E @@ -820,34 +820,34 @@ committee: restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sql: - "( - SELECT array_agg(DISTINCT user_id ORDER BY user_id) - FROM ( + sql: | + ( + SELECT array_agg(DISTINCT user_id ORDER BY user_id) + FROM ( - -- Select user_ids from committees meetings + -- Select user_ids from committees meetings - SELECT mu.user_id - FROM meeting_t AS m - INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id - WHERE m.committee_id = c.id - - UNION + SELECT mu.user_id + FROM meeting_t AS m + INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE m.committee_id = c.id - -- Select user_ids from committee managers + UNION - SELECT cmu.user_id - FROM nm_committee_manager_ids_user cmu - WHERE cmu.committee_id = c.id - - UNION + -- Select user_ids from committee managers - -- Select user_id from home committees + SELECT cmu.user_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.committee_id = c.id - SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id - ) _ - ) AS user_ids" + UNION + + -- Select user_id from home committees + + SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id + ) _ + ) AS user_ids manager_ids: type: relation-list to: user/committee_management_ids @@ -1938,12 +1938,12 @@ meeting: type: relation-list description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true - sql: + sql: | ( - SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id - WHERE mu.meeting_id = m.id + SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.meeting_id = m.id ) AS user_ids to: user/meeting_ids restriction_mode: A @@ -4227,11 +4227,11 @@ projector: required: true constant: true -# A projection is an M2M model between a projector and an element which is assigned to it. -# It can either be the current projection, a preview or a history projection (i.e. previously -# projected, with reference retained for protocolling purposes). The projection can only be -# connected to the projector once in one of these contexts. Projections are projector- and -# element-specific, meaning that, while the type of projector reference may change, neither +# A projection is an M2M model between a projector and an element which is assigned to it. +# It can either be the current projection, a preview or a history projection (i.e. previously +# projected, with reference retained for protocolling purposes). The projection can only be +# connected to the projector once in one of these contexts. Projections are projector- and +# element-specific, meaning that, while the type of projector reference may change, neither # the referenced projector, nor the content_object of the projection may be changed. projection: id: *id_field From 839fb5543213d41ffa1d308d5f99f4c41410edfc Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 17 Jul 2025 08:03:19 +0200 Subject: [PATCH 090/142] Change models.yml for new vote service based on rel-db --- models.yml | 200 ++++++++--------------------------------------------- 1 file changed, 29 insertions(+), 171 deletions(-) diff --git a/models.yml b/models.yml index 85539f7f..974f5109 100644 --- a/models.yml +++ b/models.yml @@ -448,13 +448,13 @@ user: type: relation-list to: option/content_object_id restriction_mode: A - vote_ids: + acting_vote_ids: type: relation-list - to: vote/user_id + to: vote/acting_user_id restriction_mode: A - delegated_vote_ids: + represented_vote_ids: type: relation-list - to: vote/delegated_user_id + to: vote/represented_user_id restriction_mode: A poll_candidate_ids: type: relation-list @@ -3421,32 +3421,23 @@ poll: type: string required: true restriction_mode: A - type: + visibility: type: string required: true enum: - analog - named - - pseudoanonymous - - cryptographic - restriction_mode: A - backend: - type: string - required: True - enum: *poll_backends - default: fast - restriction_mode: A - is_pseudoanonymized: - type: boolean + - open + - secret restriction_mode: A - pollmethod: + method: type: string required: true enum: - - "Y" - - "YN" - - "YNA" - - "N" + - motion + - selection + - rating + - single_transferable_vote restriction_mode: A state: type: string @@ -3457,56 +3448,15 @@ poll: - published default: created restriction_mode: A - min_votes_amount: - type: number - default: 1 - minimum: 1 - restriction_mode: A - max_votes_amount: - type: number - default: 1 - minimum: 1 - restriction_mode: A - max_votes_per_option: - type: number - default: 1 - minimum: 1 - restriction_mode: A - global_yes: - type: boolean - default: false - restriction_mode: A - global_no: - type: boolean - default: false - restriction_mode: A - global_abstain: - type: boolean - default: false + config: + type: JSON + description: Values to configure the poll. Depends on the value in poll/method. restriction_mode: A - onehundred_percent_base: - type: string + result: + type: JSON + description: Calculated result. The format depends on the value in poll/method required: true - enum: *onehundred_percent_bases - default: disabled - restriction_mode: A - votesvalid: - type: decimal(6) - restriction_mode: B - votesinvalid: - type: decimal(6) restriction_mode: B - votescast: - type: decimal(6) - restriction_mode: D - entitled_users_at_stop: - type: JSON - restriction_mode: A - has_voted_user_ids: - type: number[] - calculated: true - description: user_ids of the users, that have voted, while a poll is running. - restriction_mode: C sequential_number: type: number description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. @@ -3514,26 +3464,6 @@ poll: required: true restriction_mode: A constant: true - crypt_key: - type: string - description: base64 public key to cryptographic votes. - read_only: true - restriction_mode: A - crypt_signature: - type: string - description: base64 signature of cryptographic_key. - read_only: true - restriction_mode: A - votes_raw: - type: text - description: original form of decrypted votes. - read_only: true - restriction_mode: B - votes_signature: - type: string - description: base64 signature of votes_raw field. - read_only: true - restriction_mode: B content_object_id: type: generic-relation reference: @@ -3550,20 +3480,12 @@ poll: restriction_mode: A required: true constant: true - option_ids: + vote_ids: type: relation-list - to: option/poll_id - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - global_option_id: - type: relation - to: option/used_as_global_option_in_poll_id - reference: option + to: vote/poll_id on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - constant: true voted_ids: type: relation-list to: user/poll_voted_ids @@ -3587,98 +3509,34 @@ poll: required: true constant: true -option: - id: *id_field - weight: - type: number - default: 10000 - restriction_mode: A - text: - type: HTMLStrict - restriction_mode: A - "yes": - type: decimal(6) - restriction_mode: B - "no": - type: decimal(6) - restriction_mode: B - abstain: - type: decimal(6) - restriction_mode: B - - poll_id: - type: relation - reference: poll - to: poll/option_ids - equal_fields: meeting_id - restriction_mode: A - constant: true - used_as_global_option_in_poll_id: - type: relation - to: poll/global_option_id - equal_fields: meeting_id - restriction_mode: A - constant: true - vote_ids: - type: relation-list - to: vote/option_id - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - content_object_id: - type: generic-relation - reference: - - motion - - user - - poll_candidate_list - to: - - motion/option_ids - - user/option_ids - - poll_candidate_list/option_id - equal_fields: meeting_id - restriction_mode: A - constant: true - meeting_id: - type: relation - reference: meeting - to: meeting/option_ids - required: true - restriction_mode: A - constant: true - vote: id: *id_field weight: type: decimal(6) restriction_mode: A constant: true + default: "1.000000" value: - type: string + type: JSON restriction_mode: A constant: true - user_token: - type: string - required: true - restriction_mode: B - constant: true - - option_id: + poll_id: type: relation - reference: option - to: option/vote_ids + reference: poll + to: poll/vote_ids equal_fields: meeting_id required: true restriction_mode: A constant: true - user_id: + acting_user_id: type: relation reference: user - to: user/vote_ids + to: user/acting_user_id restriction_mode: A - delegated_user_id: + represented_user_id: type: relation reference: user - to: user/delegated_vote_ids + to: user/represented_user_id restriction_mode: A meeting_id: type: relation From 07cd4c77f33c01c33423b186c7f8aca9650c59d7 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 17 Jul 2025 08:19:44 +0200 Subject: [PATCH 091/142] generate sql --- dev/sql/schema_relational.sql | 211 +++++++++++++++------------------- models.yml | 33 +----- 2 files changed, 94 insertions(+), 150 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 7a52f967..80da1a5d 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'b582c21c91fa0895fe2cb1b77a3943be' +-- MODELS_YML_CHECKSUM = '7b13c3d504f6fb9aa5d52140553bac66' -- Database parameters @@ -855,78 +855,34 @@ CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, description text, title varchar(256) NOT NULL, - type varchar(256) NOT NULL CONSTRAINT enum_poll_type CHECK (type IN ('analog', 'named', 'pseudoanonymous', 'cryptographic')), - backend varchar(256) NOT NULL CONSTRAINT enum_poll_backend CHECK (backend IN ('long', 'fast')) DEFAULT 'fast', - is_pseudoanonymized boolean, - pollmethod varchar(256) NOT NULL CONSTRAINT enum_poll_pollmethod CHECK (pollmethod IN ('Y', 'YN', 'YNA', 'N')), + visibility varchar(256) NOT NULL CONSTRAINT enum_poll_visibility CHECK (visibility IN ('analog', 'named', 'open', 'secret')), + method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('motion', 'selection', 'rating', 'single_transferable_vote')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', - min_votes_amount integer CONSTRAINT minimum_min_votes_amount CHECK (min_votes_amount >= 1) DEFAULT 1, - max_votes_amount integer CONSTRAINT minimum_max_votes_amount CHECK (max_votes_amount >= 1) DEFAULT 1, - max_votes_per_option integer CONSTRAINT minimum_max_votes_per_option CHECK (max_votes_per_option >= 1) DEFAULT 1, - global_yes boolean DEFAULT False, - global_no boolean DEFAULT False, - global_abstain boolean DEFAULT False, - onehundred_percent_base varchar(256) NOT NULL CONSTRAINT enum_poll_onehundred_percent_base CHECK (onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'disabled', - votesvalid decimal(16,6), - votesinvalid decimal(16,6), - votescast decimal(16,6), - entitled_users_at_stop jsonb, + config jsonb, + result jsonb NOT NULL, sequential_number integer NOT NULL, - crypt_key varchar(256), - crypt_signature varchar(256), - votes_raw text, - votes_signature varchar(256), content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), - global_option_id integer UNIQUE, meeting_id integer NOT NULL ); +comment on column poll_t.config is 'Values to configure the poll. Depends on the value in poll/method.'; +comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column poll_t.crypt_key is 'base64 public key to cryptographic votes.'; -comment on column poll_t.crypt_signature is 'base64 signature of cryptographic_key.'; -comment on column poll_t.votes_raw is 'original form of decrypted votes.'; -comment on column poll_t.votes_signature is 'base64 signature of votes_raw field.'; - -/* - Fields without SQL definition for table poll - - poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema - -*/ - -CREATE TABLE option_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight integer DEFAULT 10000, - text text, - yes decimal(16,6), - no decimal(16,6), - abstain decimal(16,6), - poll_id integer, - content_object_id varchar(100), - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_poll_candidate_list_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), - meeting_id integer NOT NULL -); - - CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight decimal(16,6), - value varchar(256), - user_token varchar(256) NOT NULL, - option_id integer NOT NULL, - user_id integer, - delegated_user_id integer, + weight decimal(16,6) DEFAULT '1.000000', + value jsonb, + poll_id integer NOT NULL, + acting_user_id integer, + represented_user_id integer, meeting_id integer NOT NULL ); @@ -1346,23 +1302,48 @@ FROM organization_t o; CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, -( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( --- Select committee_ids from meetings the user is part of -SELECT m.committee_id FROM meeting_user_t AS mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id INNER JOIN meeting_t AS m ON m.id = mu.meeting_id WHERE mu.user_id = u.id -UNION --- Select committee_ids from committee managers -SELECT cmu.committee_id FROM nm_committee_manager_ids_user cmu WHERE cmu.user_id = u.id -UNION --- Select home_committee_id from user -SELECT u.home_committee_id WHERE u.home_committee_id IS NOT NULL ) _ ) AS committee_ids, +( + SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + FROM ( + + -- Select committee_ids from meetings the user is part of + + SELECT m.committee_id + FROM meeting_user_t AS mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id + + UNION + + -- Select committee_ids from committee managers + + SELECT cmu.committee_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.user_id = u.id + + UNION + + -- Select home_committee_id from user + + SELECT u.home_committee_id + WHERE u.home_committee_id IS NOT NULL + ) _ +) AS committee_ids +, (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, (select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.acting_user_id = u.id) as acting_vote_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.represented_user_id = u.id) as represented_vote_ids, (select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, -( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids +( +SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) +FROM meeting_user_t mu +INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id +WHERE mu.user_id = u.id +) AS meeting_ids + FROM user_t u; comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; @@ -1400,15 +1381,34 @@ FROM theme_t t; CREATE VIEW "committee" AS SELECT *, (select array_agg(m.id ORDER BY m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -( SELECT array_agg(DISTINCT user_id ORDER BY user_id) FROM ( +( +SELECT array_agg(DISTINCT user_id ORDER BY user_id) +FROM ( + -- Select user_ids from committees meetings -SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id + +SELECT mu.user_id +FROM meeting_t AS m +INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id +INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id +WHERE m.committee_id = c.id + UNION + -- Select user_ids from committee managers -SELECT cmu.user_id FROM nm_committee_manager_ids_user cmu WHERE cmu.committee_id = c.id + +SELECT cmu.user_id +FROM nm_committee_manager_ids_user cmu +WHERE cmu.committee_id = c.id + UNION + -- Select user_id from home committees -SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id ) _ ) AS user_ids, + +SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id +) _ +) AS user_ids +, (select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, (select array_agg(ct.id ORDER BY ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, (select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, @@ -1456,7 +1456,6 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.meeting_id = m.id) as option_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, (select array_agg(a.id ORDER BY a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, @@ -1467,7 +1466,13 @@ CREATE VIEW "meeting" AS SELECT *, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, -( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids, +( +SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) +FROM meeting_user_t mu +INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id +WHERE mu.meeting_id = m.id +) AS user_ids +, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, @@ -1571,7 +1576,6 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_motion_id = m.id) as option_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.motion_id = m.id) as change_recommendation_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, @@ -1637,19 +1641,13 @@ FROM motion_workflow_t m; CREATE VIEW "poll" AS SELECT *, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.poll_id = p.id) as vote_ids, (select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, (select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; -CREATE VIEW "option" AS SELECT *, -(select p.id from poll_t p where p.global_option_id = o.id) as used_as_global_option_in_poll_id, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.option_id = o.id) as vote_ids -FROM option_t o; - - CREATE VIEW "vote" AS SELECT * FROM vote_t v; @@ -1668,8 +1666,7 @@ CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; CREATE VIEW "poll_candidate_list" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, -(select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id +(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids FROM poll_candidate_list_t p; @@ -1878,18 +1875,11 @@ ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(i ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; -ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(acting_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(represented_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2223,11 +2213,6 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('option'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON option_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t @@ -2341,9 +2326,8 @@ SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids -SQL nt:1Gr => user/option_ids:-> option/content_object_id -SQL nt:1r => user/vote_ids:-> vote/user_id -SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id +SQL nt:1r => user/acting_vote_ids:-> vote/acting_user_id +SQL nt:1r => user/represented_vote_ids:-> vote/represented_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids SQL nts:nts => user/meeting_ids:-> meeting/user_ids @@ -2421,7 +2405,6 @@ SQL nt:1rR => meeting/motion_working_group_speaker_ids:-> motion_working_group_s SQL nt:1rR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id SQL nt:1rR => meeting/motion_state_ids:-> motion_state/meeting_id SQL nt:1rR => meeting/poll_ids:-> poll/meeting_id -SQL nt:1rR => meeting/option_ids:-> option/meeting_id SQL nt:1rR => meeting/vote_ids:-> vote/meeting_id SQL nt:1rR => meeting/assignment_ids:-> assignment/meeting_id SQL nt:1rR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id @@ -2557,7 +2540,6 @@ SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ SQL nt:1rR => motion/editor_ids:-> motion_editor/motion_id SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id -SQL nt:1Gr => motion/option_ids:-> option/content_object_id SQL nt:1rR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id @@ -2620,22 +2602,15 @@ SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/m FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids -SQL nt:1r => poll/option_ids:-> option/poll_id -FIELD 1r:1t => poll/global_option_id:-> option/used_as_global_option_in_poll_id +SQL nt:1rR => poll/vote_ids:-> vote/poll_id SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids -FIELD 1r:nt => option/poll_id:-> poll/option_ids -SQL 1t:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id -SQL nt:1rR => option/vote_ids:-> vote/option_id -FIELD 1Gr:nt,nt,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id -FIELD 1rR:nt => option/meeting_id:-> meeting/option_ids - -FIELD 1rR:nt => vote/option_id:-> option/vote_ids -FIELD 1r:nt => vote/user_id:-> user/vote_ids -FIELD 1r:nt => vote/delegated_user_id:-> user/delegated_vote_ids +FIELD 1rR:nt => vote/poll_id:-> poll/vote_ids +FIELD 1r:nt => vote/acting_user_id:-> user/acting_vote_ids +FIELD 1r:nt => vote/represented_user_id:-> user/represented_vote_ids FIELD 1rR:nt => vote/meeting_id:-> meeting/vote_ids SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id @@ -2653,7 +2628,6 @@ FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ SQL nt:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id FIELD 1rR:nt => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids -SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id FIELD 1rR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids FIELD 1r:nt => poll_candidate/user_id:-> user/poll_candidate_ids @@ -2734,9 +2708,8 @@ FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids */ /* -There are 3 errors/warnings +There are 2 errors/warnings organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema - poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema projection/content: type:JSON is marked as a calculated field and not generated in schema */ diff --git a/models.yml b/models.yml index 974f5109..c7879208 100644 --- a/models.yml +++ b/models.yml @@ -214,13 +214,6 @@ organization: saml_private_key: type: text restriction_mode: D - - vote_decrypt_public_main_key: - type: string - description: Public key from vote decrypt to validate cryptographic votes. - calculated: true - restriction_mode: A - committee_ids: type: relation-list to: committee/organization_id @@ -444,10 +437,6 @@ user: type: relation-list to: poll/voted_ids restriction_mode: A - option_ids: - type: relation-list - to: option/content_object_id - restriction_mode: A acting_vote_ids: type: relation-list to: vote/acting_user_id @@ -1792,11 +1781,6 @@ meeting: to: poll/meeting_id on_delete: CASCADE restriction_mode: B - option_ids: - type: relation-list - to: option/meeting_id - on_delete: CASCADE - restriction_mode: B vote_ids: type: relation-list to: vote/meeting_id @@ -2855,12 +2839,6 @@ motion: on_delete: CASCADE equal_fields: meeting_id restriction_mode: C - option_ids: - type: relation-list - to: option/content_object_id - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: C change_recommendation_ids: type: relation-list to: motion_change_recommendation/motion_id @@ -3531,12 +3509,12 @@ vote: acting_user_id: type: relation reference: user - to: user/acting_user_id + to: user/acting_vote_ids restriction_mode: A represented_user_id: type: relation reference: user - to: user/represented_user_id + to: user/represented_vote_ids restriction_mode: A meeting_id: type: relation @@ -3677,13 +3655,6 @@ poll_candidate_list: required: true restriction_mode: A constant: true - option_id: - type: relation - to: option/content_object_id - equal_fields: meeting_id - required: true - restriction_mode: A - constant: true poll_candidate: id: *id_field From c7071cfb34b876209f415aabc2e3e057083ba5cf Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Thu, 17 Jul 2025 08:47:11 +0200 Subject: [PATCH 092/142] [rel-db] Double precision (#286) * double precision for float instead of real --- dev/sql/schema_relational.sql | 8 ++++---- dev/src/generate_sql_schema.py | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 7a52f967..25581186 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -347,7 +347,7 @@ CREATE TABLE meeting_t ( export_csv_separator varchar(256) DEFAULT ';', export_pdf_pagenumber_alignment varchar(256) CONSTRAINT enum_meeting_export_pdf_pagenumber_alignment CHECK (export_pdf_pagenumber_alignment IN ('left', 'right', 'center')) DEFAULT 'center', export_pdf_fontsize integer CONSTRAINT enum_meeting_export_pdf_fontsize CHECK (export_pdf_fontsize IN (10, 11, 12)) DEFAULT 10, - export_pdf_line_height real CONSTRAINT minimum_export_pdf_line_height CHECK (export_pdf_line_height >= 1.0) DEFAULT 1.25, + export_pdf_line_height double precision CONSTRAINT minimum_export_pdf_line_height CHECK (export_pdf_line_height >= 1.0) DEFAULT 1.25, export_pdf_page_margin_left integer CONSTRAINT minimum_export_pdf_page_margin_left CHECK (export_pdf_page_margin_left >= 0) DEFAULT 20, export_pdf_page_margin_top integer CONSTRAINT minimum_export_pdf_page_margin_top CHECK (export_pdf_page_margin_top >= 0) DEFAULT 25, export_pdf_page_margin_right integer CONSTRAINT minimum_export_pdf_page_margin_right CHECK (export_pdf_page_margin_right >= 0) DEFAULT 20, @@ -609,8 +609,8 @@ CREATE TABLE structure_level_list_of_speakers_t ( structure_level_id integer NOT NULL, list_of_speakers_id integer NOT NULL, initial_time integer NOT NULL CONSTRAINT minimum_initial_time CHECK (initial_time >= 1), - additional_time real, - remaining_time real NOT NULL, + additional_time double precision, + remaining_time double precision NOT NULL, current_start_time timestamptz, meeting_id integer NOT NULL ); @@ -1110,7 +1110,7 @@ CREATE TABLE projector_countdown_t ( title varchar(256) NOT NULL, description varchar(256) DEFAULT '', default_time integer, - countdown_time real DEFAULT 60, + countdown_time double precision DEFAULT 60, running boolean DEFAULT False, meeting_id integer NOT NULL ); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 98e245d7..d7b92370 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1140,7 +1140,10 @@ def get_foreign_table_from_to_or_reference( "pg_type": "text", "method": GenerateCodeBlocks.get_schema_simple_types, }, - "float": {"pg_type": "real", "method": GenerateCodeBlocks.get_schema_simple_types}, + "float": { + "pg_type": "double precision", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, "decimal(6)": { "pg_type": "decimal(16,6)", "method": GenerateCodeBlocks.get_schema_simple_types, From 13c0ca5f2246f884bdd550ceb770c215001f481f Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 17 Jul 2025 15:22:19 +0200 Subject: [PATCH 093/142] [Rel-DB] Fix and unify the sql attributes (#288) --- dev/sql/schema_relational.sql | 85 ++++++++++++++++++++++++++--------- models.yml | 50 +++++++++------------ 2 files changed, 85 insertions(+), 50 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 25581186..9123dccb 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'b582c21c91fa0895fe2cb1b77a3943be' +-- MODELS_YML_CHECKSUM = '37c00fd09a72c83f2fbea74f023a6ec5' -- Database parameters @@ -1346,15 +1346,31 @@ FROM organization_t o; CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, -( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( --- Select committee_ids from meetings the user is part of -SELECT m.committee_id FROM meeting_user_t AS mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id INNER JOIN meeting_t AS m ON m.id = mu.meeting_id WHERE mu.user_id = u.id -UNION --- Select committee_ids from committee managers -SELECT cmu.committee_id FROM nm_committee_manager_ids_user cmu WHERE cmu.user_id = u.id -UNION --- Select home_committee_id from user -SELECT u.home_committee_id WHERE u.home_committee_id IS NOT NULL ) _ ) AS committee_ids, +( + SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + FROM ( + -- Select committee_ids from meetings the user is part of + SELECT m.committee_id + FROM meeting_user_t AS mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id + + UNION + + -- Select committee_ids from committee managers + SELECT cmu.committee_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.user_id = u.id + + UNION + + -- Select home_committee_id from user + SELECT u.home_committee_id + WHERE u.home_committee_id IS NOT NULL + ) _ +) AS committee_ids +, (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, (select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, @@ -1362,7 +1378,13 @@ SELECT u.home_committee_id WHERE u.home_committee_id IS NOT NULL ) _ ) AS commit (select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, (select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, -( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids +( + SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.user_id = u.id +) AS meeting_ids + FROM user_t u; comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; @@ -1400,15 +1422,30 @@ FROM theme_t t; CREATE VIEW "committee" AS SELECT *, (select array_agg(m.id ORDER BY m.id) from meeting_t m where m.committee_id = c.id) as meeting_ids, -( SELECT array_agg(DISTINCT user_id ORDER BY user_id) FROM ( --- Select user_ids from committees meetings -SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id -UNION --- Select user_ids from committee managers -SELECT cmu.user_id FROM nm_committee_manager_ids_user cmu WHERE cmu.committee_id = c.id -UNION --- Select user_id from home committees -SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id ) _ ) AS user_ids, +( + SELECT array_agg(DISTINCT user_id ORDER BY user_id) + FROM ( + -- Select user_ids from committees meetings + SELECT mu.user_id + FROM meeting_t AS m + INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE m.committee_id = c.id + + UNION + + -- Select user_ids from committee managers + SELECT cmu.user_id + FROM nm_committee_manager_ids_user cmu + WHERE cmu.committee_id = c.id + + UNION + + -- Select user_id from home committees + SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id + ) _ +) AS user_ids +, (select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, (select array_agg(ct.id ORDER BY ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, (select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, @@ -1467,7 +1504,13 @@ CREATE VIEW "meeting" AS SELECT *, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, (select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, (select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, -( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids, +( + SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.meeting_id = m.id +) AS user_ids +, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_id = m.id) as projection_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_agenda_item_list_in_meeting_id = m.id) as default_projector_agenda_item_list_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_topic_in_meeting_id = m.id) as default_projector_topic_ids, diff --git a/models.yml b/models.yml index ffff8ede..ca783cd9 100644 --- a/models.yml +++ b/models.yml @@ -399,13 +399,11 @@ user: restriction_mode: E read_only: true description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sql: + sql: | ( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( - -- Select committee_ids from meetings the user is part of - SELECT m.committee_id FROM meeting_user_t AS mu INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id @@ -415,15 +413,13 @@ user: UNION -- Select committee_ids from committee managers - SELECT cmu.committee_id FROM nm_committee_manager_ids_user cmu WHERE cmu.user_id = u.id - UNION + UNION -- Select home_committee_id from user - SELECT u.home_committee_id WHERE u.home_committee_id IS NOT NULL ) _ @@ -470,11 +466,11 @@ user: type: relation-list description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. read_only: true - sql: + sql: | ( - SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids to: meeting/user_ids @@ -820,34 +816,30 @@ committee: restriction_mode: A read_only: true description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sql: - "( + sql: | + ( SELECT array_agg(DISTINCT user_id ORDER BY user_id) FROM ( - -- Select user_ids from committees meetings - SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id - + UNION -- Select user_ids from committee managers - SELECT cmu.user_id FROM nm_committee_manager_ids_user cmu WHERE cmu.committee_id = c.id - - UNION - -- Select user_id from home committees + UNION + -- Select user_id from home committees SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id ) _ - ) AS user_ids" + ) AS user_ids manager_ids: type: relation-list to: user/committee_management_ids @@ -1938,11 +1930,11 @@ meeting: type: relation-list description: Calculated. All user ids from all users assigned to groups of this meeting. read_only: true - sql: + sql: | ( - SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids to: user/meeting_ids @@ -4227,11 +4219,11 @@ projector: required: true constant: true -# A projection is an M2M model between a projector and an element which is assigned to it. -# It can either be the current projection, a preview or a history projection (i.e. previously -# projected, with reference retained for protocolling purposes). The projection can only be -# connected to the projector once in one of these contexts. Projections are projector- and -# element-specific, meaning that, while the type of projector reference may change, neither +# A projection is an M2M model between a projector and an element which is assigned to it. +# It can either be the current projection, a preview or a history projection (i.e. previously +# projected, with reference retained for protocolling purposes). The projection can only be +# connected to the projector once in one of these contexts. Projections are projector- and +# element-specific, meaning that, while the type of projector reference may change, neither # the referenced projector, nor the content_object of the projection may be changed. projection: id: *id_field From 96d6204836dc9025d6c5ac016dee17037c6b6076 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 18 Jul 2025 12:30:30 +0200 Subject: [PATCH 094/142] move analog from visibility to method --- models.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.yml b/models.yml index c7879208..e7e2e484 100644 --- a/models.yml +++ b/models.yml @@ -3403,7 +3403,6 @@ poll: type: string required: true enum: - - analog - named - open - secret @@ -3412,6 +3411,7 @@ poll: type: string required: true enum: + - analog - motion - selection - rating From 59c42d3569c9bcad6e93f0eb676e2d9b04c8be8b Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Thu, 24 Jul 2025 17:02:48 +0200 Subject: [PATCH 095/142] generate schema --- dev/sql/schema_relational.sql | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 9123dccb..2fa1102c 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '37c00fd09a72c83f2fbea74f023a6ec5' +-- MODELS_YML_CHECKSUM = 'b7699ab67c248821ddc8a885f9041d0d' -- Database parameters @@ -468,6 +468,7 @@ This email was generated automatically.', poll_default_method varchar(256), poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', poll_default_backend varchar(256) CONSTRAINT enum_meeting_poll_default_backend CHECK (poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', + poll_default_live_voting_enabled boolean DEFAULT False, poll_couple_countdown boolean DEFAULT True, logo_projector_main_id integer UNIQUE, logo_projector_header_id integer UNIQUE, @@ -501,6 +502,7 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; +comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default 'poll.live_voting_enabled' option suggested to user. Is not used in the validations.'; CREATE TABLE structure_level_t ( @@ -871,6 +873,7 @@ CREATE TABLE poll_t ( votesinvalid decimal(16,6), votescast decimal(16,6), entitled_users_at_stop jsonb, + live_voting_enabled boolean DEFAULT False, sequential_number integer NOT NULL, crypt_key varchar(256), crypt_signature varchar(256), @@ -887,6 +890,7 @@ CREATE TABLE poll_t ( +comment on column poll_t.live_voting_enabled is 'If true, the vote service sends the votes of the users to the autoupdate service.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; comment on column poll_t.crypt_key is 'base64 public key to cryptographic votes.'; comment on column poll_t.crypt_signature is 'base64 signature of cryptographic_key.'; @@ -896,7 +900,7 @@ comment on column poll_t.votes_signature is 'base64 signature of votes_raw field /* Fields without SQL definition for table poll - poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema + poll/live_votes: type:JSON is marked as a calculated field and not generated in schema */ @@ -2779,7 +2783,7 @@ FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids /* There are 3 errors/warnings organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema - poll/has_voted_user_ids: type:number[] is marked as a calculated field and not generated in schema + poll/live_votes: type:JSON is marked as a calculated field and not generated in schema projection/content: type:JSON is marked as a calculated field and not generated in schema */ From a45bb6d29d12407d0b03675bbe55fb888548a5a8 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 26 Jul 2025 07:39:59 +0200 Subject: [PATCH 096/142] cleanups --- models.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/models.yml b/models.yml index e7e2e484..13b21039 100644 --- a/models.yml +++ b/models.yml @@ -3392,20 +3392,12 @@ motion_workflow: poll: id: *id_field - description: - type: text - restriction_mode: A title: type: string required: true restriction_mode: A - visibility: - type: string - required: true - enum: - - named - - open - - secret + description: + type: text restriction_mode: A method: type: string @@ -3417,6 +3409,17 @@ poll: - rating - single_transferable_vote restriction_mode: A + config: + type: JSON + description: Values to configure the poll. Depends on the value in poll/method. + restriction_mode: A + visibility: + type: string + enum: + - named + - open + - secret + restriction_mode: A state: type: string enum: @@ -3426,10 +3429,6 @@ poll: - published default: created restriction_mode: A - config: - type: JSON - description: Values to configure the poll. Depends on the value in poll/method. - restriction_mode: A result: type: JSON description: Calculated result. The format depends on the value in poll/method From 13ee68551ed72929fa50df8346dcb7367ad713df Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Mon, 28 Jul 2025 12:05:32 +0200 Subject: [PATCH 097/142] Fix sql comments with quotes (#292) * fix sql comments with quote * catch errors in action script * fix base_data.sql --- .github/workflows/test-sql.yml | 6 +- dev/scripts/apply_base_data.sh | 2 +- dev/scripts/apply_db_schema.sh | 2 +- dev/sql/base_data.sql | 268 +++++++++++++++++++-------------- dev/sql/schema_relational.sql | 4 +- dev/src/generate_sql_schema.py | 1 + models.yml | 2 +- 7 files changed, 164 insertions(+), 121 deletions(-) diff --git a/.github/workflows/test-sql.yml b/.github/workflows/test-sql.yml index 5c21ace5..e7641ea5 100644 --- a/.github/workflows/test-sql.yml +++ b/.github/workflows/test-sql.yml @@ -29,7 +29,8 @@ jobs: apt-get install --yes --no-install-recommends postgresql-client - name: apply schema - run: sh dev/scripts/apply_db_schema.sh + shell: bash + run: bash dev/scripts/apply_db_schema.sh env: DATABASE_HOST: postgres DATABASE_PORT: 5432 @@ -38,7 +39,8 @@ jobs: PGPASSWORD: password - name: insert init data - run: sh dev/scripts/apply_base_data.sh + shell: bash + run: bash dev/scripts/apply_base_data.sh env: DATABASE_HOST: postgres DATABASE_PORT: 5432 diff --git a/dev/scripts/apply_base_data.sh b/dev/scripts/apply_base_data.sh index 005d3b71..442a4c9d 100755 --- a/dev/scripts/apply_base_data.sh +++ b/dev/scripts/apply_base_data.sh @@ -1,4 +1,4 @@ #!/bin/bash cd "$(dirname "$0")" -psql -1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/base_data.sql +psql -1 -v ON_ERROR_STOP=1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/base_data.sql diff --git a/dev/scripts/apply_db_schema.sh b/dev/scripts/apply_db_schema.sh index d7063960..59d66d12 100755 --- a/dev/scripts/apply_db_schema.sh +++ b/dev/scripts/apply_db_schema.sh @@ -1,4 +1,4 @@ #!/bin/bash cd "$(dirname "$0")" -psql -1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/schema_relational.sql +psql -1 -v ON_ERROR_STOP=1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/schema_relational.sql diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index a932ed46..95d658ed 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -1,128 +1,168 @@ -- This script can only be used for an empty database without used sequences. +insert into gender_t (name) values('female'); +insert into user_t (username,gender_id) values('tom',1); --relation --relation-list gender_ids +BEGIN; + INSERT INTO motion_state_t (id,name,weight,workflow_id,meeting_id) + VALUES (1,'motionState1',1,1,1); + INSERT INTO motion_workflow_t (id, name, sequential_number, first_state_id, meeting_id) + VALUES (1,'workflow1',1,1,1); + INSERT INTO meeting_t (id,name,motions_default_workflow_id,motions_default_amendment_workflow_id,committee_id,reference_projector_id,default_group_id) + VALUES (1,'name',1,1,1,1,1); + INSERT INTO organization_tag_t(id,name,color) + VALUES(1,'tagA','#cc3b03'); --generic-relation-list tagged_ids + Insert Into committee(id, name, default_meeting_id) + Values(1,'plenum',1); --relation-list organization_tag_ids --relation 1:1 default_meeting_id + INSERT INTO projector_t (id,sequential_number,meeting_id) + VALUES (1,1,1); + INSERT INTO group_t (id,name,meeting_id) + VALUES (1,'gruppe1',1); +COMMIT; + +INSERT INTO organization_tag_t (id,name,color) + VALUES (2,'bunt','#ffffff'); +INSERT INTO gm_organization_tag_tagged_ids(organization_tag_id,tagged_id) + VALUES(2,'meeting/1'); + +INSERT INTO topic_t (id,title, sequential_number, meeting_id) + VALUES (1, 'Thema3', 1, 1); +INSERT INTO agenda_item_t (content_object_id, meeting_id) + VALUES ('topic/1',1);--agenda_item.content_object_id:topic.agenda_item_id gr:r + +INSERT INTO option_t (id,content_object_id,meeting_id) + VALUES (1, 'user/1', 1);--rl:gr user.option_id:option.content_object_id + +INSERT INTO nm_committee_manager_ids_user + VALUES (1,1); --rl:rl committee_ids:user_ids + INSERT INTO - theme (id, name, accent_500, primary_500, warn_500) + theme_t (id, name) VALUES - (1, 'standard theme',); + (1, 'standard theme'); INSERT INTO - organization (name, theme_id, default_language) + organization_t (name, theme_id) VALUES ('Intevation', 1); INSERT INTO - committee (id, name) + committee_t (id, name) VALUES - (1, 'committee'); + (2, 'committee'); -INSERT INTO - meeting ( - id, - default_group_id, - admin_group_id, - motions_default_workflow_id, - motions_default_amendment_workflow_id, - committee_id, - reference_projector_id, - name, - language - ) -VALUES - (1, 1, 2, 1, 1, 1, 1, 'meeting'); +BEGIN; + INSERT INTO + meeting_t ( + id, + default_group_id, + admin_group_id, + motions_default_workflow_id, + motions_default_amendment_workflow_id, + committee_id, + reference_projector_id, + name + ) + VALUES + (2, 2, 2, 2, 2, 1, 2, 'meeting'); -INSERT INTO - -- TODO Replace it with "group" after this is fixed: https://github.com/OpenSlides/openslides-meta/issues/243 - group_ (id, name, meeting_id, permissions) -VALUES - ( - 1, - 'Default', - 1, - '{ - "agenda_item.can_see", - "assignment.can_see", - "meeting.can_see_autopilot", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see" - }' - ), - (2, 'Admin', 1, DEFAULT); + INSERT INTO + -- TODO Replace it with "group" after this is fixed: https://github.com/OpenSlides/openslides-meta/issues/243 + group_t (id, name, meeting_id, permissions) + VALUES + ( + 2, + 'Default', + 1, + '{ + "agenda_item.can_see", + "assignment.can_see", + "meeting.can_see_autopilot", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see" + }' + ), + (3, 'Admin', 1, DEFAULT); -INSERT INTO - motion_workflow ( - id, - name, - sequential_number, - first_state_id, - meeting_id - ) -VALUES - (1, 'Simple Workflow', 1, 1, 1); + INSERT INTO + motion_workflow_t ( + id, + name, + sequential_number, + first_state_id, + meeting_id + ) + VALUES + (2, 'Simple Workflow', 1, 2, 2); -INSERT INTO - motion_state ( - id, - name, - weight, - workflow_id, - meeting_id, - allow_create_poll, - allow_support, - set_workflow_timestamp, - recommendation_label, - css_class, - merge_amendment_into_final - ) -VALUES - ( - 1, - 'submitted', - 1, - 1, - 1, - true, - true, - true, - 'Submitted', - 'grey', - 'do_not_merge' - ), - ( - 2, - 'accepted', - 2, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'Acceptance', - 'green', - 'do_merge' - ), - ( - 3, - 'rejected', - 3, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'Rejection', - 'red', - 'do_not_merge' - ), - ( - 4, - 'not decided', - 4, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'No decision', - 'grey', - 'do_not_merge' - ); + INSERT INTO + motion_state_t ( + id, + name, + weight, + workflow_id, + meeting_id, + allow_create_poll, + allow_support, + set_workflow_timestamp, + recommendation_label, + css_class, + merge_amendment_into_final + ) + VALUES + ( + 2, + 'submitted', + 1, + 1, + 1, + true, + true, + true, + 'Submitted', + 'grey', + 'do_not_merge' + ), + ( + 3, + 'accepted', + 2, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Acceptance', + 'green', + 'do_merge' + ), + ( + 4, + 'rejected', + 3, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Rejection', + 'red', + 'do_not_merge' + ), + ( + 5, + 'not decided', + 4, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'No decision', + 'grey', + 'do_not_merge' + ); + + INSERT INTO projector_t (id,sequential_number,meeting_id) + VALUES (2,2,1); + +COMMIT; \ No newline at end of file diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 2fa1102c..6c19a35b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'b7699ab67c248821ddc8a885f9041d0d' +-- MODELS_YML_CHECKSUM = '8f69f7d0541bd14d340ae817ba75b1e3' -- Database parameters @@ -502,7 +502,7 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default 'poll.live_voting_enabled' option suggested to user. Is not used in the validations.'; +comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.live_voting_enabled'' option suggested to user. Is not used in the validations.'; CREATE TABLE structure_level_t ( diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index d7b92370..747d6125 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1052,6 +1052,7 @@ def get_initials( @staticmethod def get_post_view_comment(entity_name: str, fname: str, comment: str) -> str: + comment = comment.replace("'", "''") return f"comment on column {entity_name}.{fname} is '{comment}';\n" @staticmethod diff --git a/models.yml b/models.yml index 5c0fc4de..24cb2189 100644 --- a/models.yml +++ b/models.yml @@ -90,7 +90,7 @@ # - You can add JSON Schema properties to the fields like `enum`, `description`, # `items`, `maxLength` and `minimum` # Additional properties: -# - The property `read_only` describes a field that can not be changed by an action. +# - The property `read_only` describes a field that can not directly be changed by an action. # - The property `default` describes the default value that is used for new objects. # - The property `required` describes that this field can not be null or an empty # string. If this field is given it must have some content. On relation and generic-relation From dd23ffc392ceca5cb10b51f40d947408bb3a5d6b Mon Sep 17 00:00:00 2001 From: Kasimir Klinger <77665830+LinKaKling@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:46:24 +0200 Subject: [PATCH 098/142] Related models notification log trigger (#256) * adds triggers that log changes of models and related models --- dev/sql/base_data.sql | 11 +- dev/sql/schema_relational.sql | 840 +++++++++++++++++++++++++++------ dev/src/generate_sql_schema.py | 181 ++++++- dev/src/helper_get_names.py | 37 +- models.yml | 12 +- 5 files changed, 915 insertions(+), 166 deletions(-) diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index 95d658ed..892dc306 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -1,6 +1,6 @@ -- This script can only be used for an empty database without used sequences. -insert into gender_t (name) values('female'); -insert into user_t (username,gender_id) values('tom',1); --relation --relation-list gender_ids +INSERT INTO gender_t (name) VALUES('female'); +INSERT INTO user_t (username,gender_id) VALUES('tom',1); --relation --relation-list gender_ids BEGIN; INSERT INTO motion_state_t (id,name,weight,workflow_id,meeting_id) VALUES (1,'motionState1',1,1,1); @@ -20,7 +20,7 @@ COMMIT; INSERT INTO organization_tag_t (id,name,color) VALUES (2,'bunt','#ffffff'); -INSERT INTO gm_organization_tag_tagged_ids(organization_tag_id,tagged_id) +INSERT INTO gm_organization_tag_tagged_ids_t(organization_tag_id,tagged_id) VALUES(2,'meeting/1'); INSERT INTO topic_t (id,title, sequential_number, meeting_id) @@ -31,7 +31,7 @@ INSERT INTO agenda_item_t (content_object_id, meeting_id) INSERT INTO option_t (id,content_object_id,meeting_id) VALUES (1, 'user/1', 1);--rl:gr user.option_id:option.content_object_id -INSERT INTO nm_committee_manager_ids_user +INSERT INTO nm_committee_manager_ids_user_t VALUES (1,1); --rl:rl committee_ids:user_ids INSERT INTO @@ -65,7 +65,6 @@ BEGIN; (2, 2, 2, 2, 2, 1, 2, 'meeting'); INSERT INTO - -- TODO Replace it with "group" after this is fixed: https://github.com/OpenSlides/openslides-meta/issues/243 group_t (id, name, meeting_id, permissions) VALUES ( @@ -165,4 +164,4 @@ BEGIN; INSERT INTO projector_t (id,sequential_number,meeting_id) VALUES (2,2,1); -COMMIT; \ No newline at end of file +COMMIT; diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 6c19a35b..26098aff 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '8f69f7d0541bd14d340ae817ba75b1e3' +-- MODELS_YML_CHECKSUM = 'f7b30c2a399c24e787a7d4864c19c03f' -- Database parameters @@ -62,17 +62,19 @@ $not_null_trigger$ language plpgsql; CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; - operation TEXT; - fqid TEXT; + operation_var TEXT; + fqid_var TEXT; BEGIN escaped_table_name := TG_ARGV[0]; - operation := LOWER(TG_OP); - fqid := escaped_table_name || '/' || NEW.id; + operation_var := LOWER(TG_OP); + fqid_var := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - fqid = escaped_table_name || '/' || OLD.id; + fqid_var = escaped_table_name || '/' || OLD.id; END IF; - INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) VALUES (operation, fqid, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) + VALUES (operation_var, fqid_var, pg_current_xact_id(), 'now') + ON CONFLICT (operation,fqid,xact_id) DO NOTHING; RETURN NULL; -- AFTER TRIGGER needs no return END; $log_modified_trigger$ LANGUAGE plpgsql; @@ -102,12 +104,49 @@ BEGIN END; $notify_trigger$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION log_modified_related_models() +RETURNS trigger AS $log_modified_related_trigger$ +DECLARE + operation_var TEXT; + fqid_var TEXT; + ref_column TEXT; + foreign_table TEXT; + foreign_id TEXT; + i INTEGER := 0; +BEGIN + operation_var := LOWER(TG_OP); + + WHILE i < TG_NARGS LOOP + foreign_table := TG_ARGV[i]; + ref_column := TG_ARGV[i+1]; + + IF (TG_OP = 'DELETE') THEN + EXECUTE format('SELECT ($1).%I', ref_column) INTO foreign_id USING OLD; + ELSE + EXECUTE format('SELECT ($1).%I', ref_column) INTO foreign_id USING NEW; + END IF; + + IF foreign_id IS NOT NULL THEN + fqid_var := foreign_table || '/' || foreign_id; + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) + VALUES (operation_var, fqid_var, pg_current_xact_id(), now()) + ON CONFLICT (operation,fqid,xact_id) DO NOTHING; + END IF; + + i := i + 2; + END LOOP; + + RETURN NULL; -- AFTER TRIGGER needs no return +END; +$log_modified_related_trigger$ LANGUAGE plpgsql; + CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, operation varchar(32), - fqid varchar(256), + fqid varchar(256) NOT NULL, xact_id xid8, - timestamp timestamptz + timestamp timestamptz, + CONSTRAINT unique_fqid_xact_id_operation UNIQUE (operation,fqid,xact_id) ); @@ -1173,19 +1212,19 @@ CREATE TABLE import_preview_t ( -- Intermediate table definitions -CREATE TABLE nm_meeting_user_supported_motion_ids_motion ( +CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, motion_id) ); -CREATE TABLE nm_meeting_user_structure_level_ids_structure_level ( +CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, structure_level_id) ); -CREATE TABLE gm_organization_tag_tagged_ids ( +CREATE TABLE gm_organization_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, @@ -1195,67 +1234,67 @@ CREATE TABLE gm_organization_tag_tagged_ids ( CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); -CREATE TABLE nm_committee_manager_ids_user ( +CREATE TABLE nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); -CREATE TABLE nm_committee_all_child_ids_committee ( +CREATE TABLE nm_committee_all_child_ids_committee_t ( all_child_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, all_parent_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (all_child_id, all_parent_id) ); -CREATE TABLE nm_committee_forward_to_committee_ids_committee ( +CREATE TABLE nm_committee_forward_to_committee_ids_committee_t ( forward_to_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); -CREATE TABLE nm_meeting_present_user_ids_user ( +CREATE TABLE nm_meeting_present_user_ids_user_t ( meeting_id integer NOT NULL REFERENCES meeting_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_id, user_id) ); -CREATE TABLE nm_group_meeting_user_ids_meeting_user ( +CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_user_id) ); -CREATE TABLE nm_group_mmagi_meeting_mediafile ( +CREATE TABLE nm_group_mmagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); -CREATE TABLE nm_group_mmiagi_meeting_mediafile ( +CREATE TABLE nm_group_mmiagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); -CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section ( +CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section ( +CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); -CREATE TABLE nm_group_poll_ids_poll ( +CREATE TABLE nm_group_poll_ids_poll_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, poll_id) ); -CREATE TABLE gm_tag_tagged_ids ( +CREATE TABLE gm_tag_tagged_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, tag_id integer NOT NULL REFERENCES tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, tagged_id varchar(100) NOT NULL, @@ -1266,19 +1305,19 @@ CREATE TABLE gm_tag_tagged_ids ( CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); -CREATE TABLE nm_motion_all_derived_motion_ids_motion ( +CREATE TABLE nm_motion_all_derived_motion_ids_motion_t ( all_derived_motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, all_origin_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (all_derived_motion_id, all_origin_id) ); -CREATE TABLE nm_motion_identical_motion_ids_motion ( +CREATE TABLE nm_motion_identical_motion_ids_motion_t ( identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); -CREATE TABLE gm_motion_state_extension_reference_ids ( +CREATE TABLE gm_motion_state_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, state_extension_reference_id varchar(100) NOT NULL, @@ -1287,7 +1326,7 @@ CREATE TABLE gm_motion_state_extension_reference_ids ( CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); -CREATE TABLE gm_motion_recommendation_extension_reference_ids ( +CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, recommendation_extension_reference_id varchar(100) NOT NULL, @@ -1296,19 +1335,19 @@ CREATE TABLE gm_motion_recommendation_extension_reference_ids ( CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); -CREATE TABLE nm_motion_state_next_state_ids_motion_state ( +CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( next_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, previous_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (next_state_id, previous_state_id) ); -CREATE TABLE nm_poll_voted_ids_user ( +CREATE TABLE nm_poll_voted_ids_user_t ( poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE gm_meeting_mediafile_attachment_ids ( +CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, attachment_id varchar(100) NOT NULL, @@ -1319,13 +1358,13 @@ CREATE TABLE gm_meeting_mediafile_attachment_ids ( CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); -CREATE TABLE nm_chat_group_read_group_ids_group ( +CREATE TABLE nm_chat_group_read_group_ids_group_t ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); -CREATE TABLE nm_chat_group_write_group_ids_group ( +CREATE TABLE nm_chat_group_write_group_ids_group_t ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) @@ -1349,14 +1388,14 @@ FROM organization_t o; CREATE VIEW "user" AS SELECT *, -(select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user n where n.user_id = u.id) as is_present_in_meeting_ids, +(select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, ( SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) FROM ( -- Select committee_ids from meetings the user is part of SELECT m.committee_id FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id INNER JOIN meeting_t AS m ON m.id = mu.meeting_id WHERE mu.user_id = u.id @@ -1364,7 +1403,7 @@ CREATE VIEW "user" AS SELECT *, -- Select committee_ids from committee managers SELECT cmu.committee_id - FROM nm_committee_manager_ids_user cmu + FROM nm_committee_manager_ids_user_t cmu WHERE cmu.user_id = u.id UNION @@ -1375,9 +1414,9 @@ CREATE VIEW "user" AS SELECT *, ) _ ) AS committee_ids , -(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user n where n.user_id = u.id) as committee_management_ids, +(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, (select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, @@ -1385,7 +1424,7 @@ CREATE VIEW "user" AS SELECT *, ( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids @@ -1397,15 +1436,15 @@ comment on column "user".meeting_ids is 'Calculated. All ids from meetings calcu CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(n.motion_id ORDER BY n.motion_id) from nm_meeting_user_supported_motion_ids_motion n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(n.motion_id ORDER BY n.motion_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.meeting_user_id = m.id) as supported_motion_ids, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, (select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, (select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user n where n.meeting_user_id = m.id) as group_ids, -(select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level n where n.meeting_user_id = m.id) as structure_level_ids +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids FROM meeting_user_t m; @@ -1415,7 +1454,7 @@ FROM gender_t g; CREATE VIEW "organization_tag" AS SELECT *, -(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_organization_tag_tagged_ids g where g.organization_tag_id = o.id) as tagged_ids +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids FROM organization_tag_t o; @@ -1433,14 +1472,14 @@ CREATE VIEW "committee" AS SELECT *, SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id UNION -- Select user_ids from committee managers SELECT cmu.user_id - FROM nm_committee_manager_ids_user cmu + FROM nm_committee_manager_ids_user_t cmu WHERE cmu.committee_id = c.id UNION @@ -1450,14 +1489,14 @@ CREATE VIEW "committee" AS SELECT *, ) _ ) AS user_ids , -(select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user n where n.committee_id = c.id) as manager_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_committee_manager_ids_user_t n where n.committee_id = c.id) as manager_ids, (select array_agg(ct.id ORDER BY ct.id) from committee_t ct where ct.parent_id = c.id) as child_ids, -(select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee n where n.all_child_id = c.id) as all_parent_ids, -(select array_agg(n.all_child_id ORDER BY n.all_child_id) from nm_committee_all_child_ids_committee n where n.all_parent_id = c.id) as all_child_ids, +(select array_agg(n.all_parent_id ORDER BY n.all_parent_id) from nm_committee_all_child_ids_committee_t n where n.all_child_id = c.id) as all_parent_ids, +(select array_agg(n.all_child_id ORDER BY n.all_child_id) from nm_committee_all_child_ids_committee_t n where n.all_parent_id = c.id) as all_child_ids, (select array_agg(u.id ORDER BY u.id) from user_t u where u.home_committee_id = c.id) as native_user_ids, -(select array_agg(n.forward_to_committee_id ORDER BY n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, -(select array_agg(n.receive_forwardings_from_committee_id ORDER BY n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, -(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_committee_id = c.id) as organization_tag_ids +(select array_agg(n.forward_to_committee_id ORDER BY n.forward_to_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.receive_forwardings_from_committee_id = c.id) as forward_to_committee_ids, +(select array_agg(n.receive_forwardings_from_committee_id ORDER BY n.receive_forwardings_from_committee_id) from nm_committee_forward_to_committee_ids_committee_t n where n.forward_to_committee_id = c.id) as receive_forwardings_from_committee_ids, +(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_committee_id = c.id) as organization_tag_ids FROM committee_t c; comment on column "committee".user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; @@ -1506,12 +1545,12 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_id = m.id) as chat_message_ids, (select array_agg(s.id ORDER BY s.id) from structure_level_t s where s.meeting_id = m.id) as structure_level_ids, (select c.id from committee_t c where c.default_meeting_id = m.id) as default_meeting_for_committee_id, -(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, -(select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user n where n.meeting_id = m.id) as present_user_ids, +(select array_agg(g.organization_tag_id ORDER BY g.organization_tag_id) from gm_organization_tag_tagged_ids_t g where g.tagged_id_meeting_id = m.id) as organization_tag_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_meeting_present_user_ids_user_t n where n.meeting_id = m.id) as present_user_ids, ( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids , @@ -1532,28 +1571,28 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids FROM meeting_t m; -comment on column "meeting".is_active_in_organization_id is 'Backrelation and boolean flag at once'; -comment on column "meeting".is_archived_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column "meeting_t".is_active_in_organization_id is 'Backrelation and boolean flag at once'; +comment on column "meeting_t".is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column "meeting".user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; CREATE VIEW "structure_level" AS SELECT *, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, (select array_agg(sl.id ORDER BY sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids FROM structure_level_t s; CREATE VIEW "group" AS SELECT *, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user n where n.group_id = g.id) as meeting_user_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, (select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, (select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, (select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, -(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_access_group_ids, -(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, -(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.group_id = g.id) as read_comment_section_ids, -(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.group_id = g.id) as write_comment_section_ids, -(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_read_group_ids_group n where n.group_id = g.id) as read_chat_group_ids, -(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_write_group_ids_group n where n.group_id = g.id) as write_chat_group_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_group_poll_ids_poll n where n.group_id = g.id) as poll_ids +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_access_group_ids, +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_write_group_ids_group_t n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids FROM group_t g; comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; @@ -1562,13 +1601,13 @@ CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; CREATE VIEW "tag" AS SELECT *, -(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_tag_tagged_ids g where g.tag_id = t.id) as tagged_ids +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids FROM tag_t t; CREATE VIEW "agenda_item" AS SELECT *, (select array_agg(ai.id ORDER BY ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids FROM agenda_item_t a; @@ -1594,7 +1633,7 @@ CREATE VIEW "speaker" AS SELECT * FROM speaker_t s; CREATE VIEW "topic" AS SELECT *, -(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, (select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, @@ -1606,15 +1645,15 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.lead_motion_id = m.id) as amendment_ids, (select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.sort_parent_id = m.id) as sort_child_ids, (select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.origin_id = m.id) as derived_motion_ids, -(select array_agg(n.all_origin_id ORDER BY n.all_origin_id) from nm_motion_all_derived_motion_ids_motion n where n.all_derived_motion_id = m.id) as all_origin_ids, -(select array_agg(n.all_derived_motion_id ORDER BY n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion n where n.all_origin_id = m.id) as all_derived_motion_ids, -(select array_cat((select array_agg(n.identical_motion_id_1 ORDER BY n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2 ORDER BY n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, -(select array_agg(g.state_extension_reference_id ORDER BY g.state_extension_reference_id) from gm_motion_state_extension_reference_ids g where g.motion_id = m.id) as state_extension_reference_ids, -(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_state_extension_reference_ids g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, -(select array_agg(g.recommendation_extension_reference_id ORDER BY g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids g where g.motion_id = m.id) as recommendation_extension_reference_ids, -(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_recommendation_extension_reference_ids g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, +(select array_agg(n.all_origin_id ORDER BY n.all_origin_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_derived_motion_id = m.id) as all_origin_ids, +(select array_agg(n.all_derived_motion_id ORDER BY n.all_derived_motion_id) from nm_motion_all_derived_motion_ids_motion_t n where n.all_origin_id = m.id) as all_derived_motion_ids, +(select array_cat((select array_agg(n.identical_motion_id_1 ORDER BY n.identical_motion_id_1) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_2 = m.id), (select array_agg(n.identical_motion_id_2 ORDER BY n.identical_motion_id_2) from nm_motion_identical_motion_ids_motion_t n where n.identical_motion_id_1 = m.id))) as identical_motion_ids, +(select array_agg(g.state_extension_reference_id ORDER BY g.state_extension_reference_id) from gm_motion_state_extension_reference_ids_t g where g.motion_id = m.id) as state_extension_reference_ids, +(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_state_extension_reference_ids_t g where g.state_extension_reference_id_motion_id = m.id) as referenced_in_motion_state_extension_ids, +(select array_agg(g.recommendation_extension_reference_id ORDER BY g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids_t g where g.motion_id = m.id) as recommendation_extension_reference_ids, +(select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_recommendation_extension_reference_ids_t g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.motion_id = m.id) as supporter_meeting_user_ids, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, @@ -1623,8 +1662,8 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_motion_id = m.id) as list_of_speakers_id, -(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_motion_id = m.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_motion_id = m.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids FROM motion_t m; @@ -1644,8 +1683,8 @@ CREATE VIEW "motion_comment" AS SELECT * FROM motion_comment_t m; CREATE VIEW "motion_comment_section" AS SELECT *, (select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.section_id = m.id) as comment_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_read_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as read_group_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_write_comment_section_ids_motion_comment_section n where n.motion_comment_section_id = m.id) as write_group_ids +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as read_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.motion_comment_section_id = m.id) as write_group_ids FROM motion_comment_section_t m; @@ -1668,8 +1707,8 @@ CREATE VIEW "motion_change_recommendation" AS SELECT * FROM motion_change_recomm CREATE VIEW "motion_state" AS SELECT *, (select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.submitter_withdraw_state_id = m.id) as submitter_withdraw_back_ids, -(select array_agg(n.next_state_id ORDER BY n.next_state_id) from nm_motion_state_next_state_ids_motion_state n where n.previous_state_id = m.id) as next_state_ids, -(select array_agg(n.previous_state_id ORDER BY n.previous_state_id) from nm_motion_state_next_state_ids_motion_state n where n.next_state_id = m.id) as previous_state_ids, +(select array_agg(n.next_state_id ORDER BY n.next_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.previous_state_id = m.id) as next_state_ids, +(select array_agg(n.previous_state_id ORDER BY n.previous_state_id) from nm_motion_state_next_state_ids_motion_state_t n where n.next_state_id = m.id) as previous_state_ids, (select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.state_id = m.id) as motion_ids, (select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.recommendation_id = m.id) as motion_recommendation_ids, (select mw.id from motion_workflow_t mw where mw.first_state_id = m.id) as first_state_of_workflow_id @@ -1685,8 +1724,8 @@ FROM motion_workflow_t m; CREATE VIEW "poll" AS SELECT *, (select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, -(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user n where n.poll_id = p.id) as voted_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, (select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; @@ -1705,8 +1744,8 @@ CREATE VIEW "assignment" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, (select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, (select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids FROM assignment_t a; @@ -1730,11 +1769,11 @@ FROM mediafile_t m; CREATE VIEW "meeting_mediafile" AS SELECT *, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmiagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmagi_meeting_mediafile n where n.meeting_mediafile_id = m.id) as access_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmiagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as access_group_ids, (select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, -(select array_agg(g.attachment_id ORDER BY g.attachment_id) from gm_meeting_mediafile_attachment_ids g where g.meeting_mediafile_id = m.id) as attachment_ids, +(select array_agg(g.attachment_id ORDER BY g.attachment_id) from gm_meeting_mediafile_attachment_ids_t g where g.meeting_mediafile_id = m.id) as attachment_ids, (select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, (select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, @@ -1780,8 +1819,8 @@ FROM projector_countdown_t p; CREATE VIEW "chat_group" AS SELECT *, (select array_agg(cm.id ORDER BY cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_read_group_ids_group n where n.chat_group_id = c.id) as read_group_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_write_group_ids_group n where n.chat_group_id = c.id) as write_group_ids +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids FROM chat_group_t c; @@ -2120,232 +2159,761 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa -- Create triggers for notify -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_t +CREATE TRIGGER tr_log_organization AFTER INSERT OR UPDATE OR DELETE ON organization_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON user_t +CREATE TRIGGER tr_log_organization_t_theme_id AFTER INSERT OR UPDATE OF theme_id OR DELETE ON organization_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('theme', 'theme_id'); + +CREATE TRIGGER tr_log_user AFTER INSERT OR UPDATE OR DELETE ON user_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('user'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +CREATE TRIGGER tr_log_user_t_gender_id AFTER INSERT OR UPDATE OF gender_id OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('gender', 'gender_id'); +CREATE TRIGGER tr_log_user_t_home_committee_id AFTER INSERT OR UPDATE OF home_committee_id OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'home_committee_id'); + +CREATE TRIGGER tr_log_meeting_user AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_user'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON gender_t +CREATE TRIGGER tr_log_meeting_user_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_meeting_user_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_nm_meeting_user_supported_motion_ids_motion_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_supported_motion_ids_motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','motion','motion_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_supported_motion_ids_motion_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); + +CREATE TRIGGER tr_log_nm_meeting_user_structure_level_ids_structure_level_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','structure_level','structure_level_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_gender AFTER INSERT OR UPDATE OR DELETE ON gender_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('gender'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gender_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +CREATE TRIGGER tr_log_organization_tag AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization_tag'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON theme_t + +CREATE TRIGGER tr_log_tagged_id_committee_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_committee_id OR DELETE ON gm_organization_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','committee','tagged_id_committee_id'); + +CREATE TRIGGER tr_log_tagged_id_meeting_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_meeting_id OR DELETE ON gm_organization_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','meeting','tagged_id_meeting_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_organization_tag_tagged_ids_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_theme AFTER INSERT OR UPDATE OR DELETE ON theme_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('theme'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON theme_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON committee_t +CREATE TRIGGER tr_log_committee AFTER INSERT OR UPDATE OR DELETE ON committee_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('committee'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON committee_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_t +CREATE TRIGGER tr_log_committee_t_default_meeting_id AFTER INSERT OR UPDATE OF default_meeting_id OR DELETE ON committee_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'default_meeting_id'); + +CREATE TRIGGER tr_log_nm_committee_manager_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_committee_manager_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee','committee_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_committee_manager_ids_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_committee_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON committee_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'parent_id'); + +CREATE TRIGGER tr_log_nm_committee_all_child_ids_committee_t AFTER INSERT OR UPDATE OR DELETE ON nm_committee_all_child_ids_committee_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee','all_child_id','committee','all_parent_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_committee_all_child_ids_committee_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_committee_forward_to_committee_ids_committee_t AFTER INSERT OR UPDATE OR DELETE ON nm_committee_forward_to_committee_ids_committee_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee','forward_to_committee_id','committee','receive_forwardings_from_committee_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_committee_forward_to_committee_ids_committee_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_meeting AFTER INSERT OR UPDATE OR DELETE ON meeting_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +CREATE TRIGGER tr_log_meeting_t_is_active_in_organization_id AFTER INSERT OR UPDATE OF is_active_in_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_active_in_organization_id'); +CREATE TRIGGER tr_log_meeting_t_is_archived_in_organization_id AFTER INSERT OR UPDATE OF is_archived_in_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_archived_in_organization_id'); +CREATE TRIGGER tr_log_meeting_t_template_for_organization_id AFTER INSERT OR UPDATE OF template_for_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'template_for_organization_id'); +CREATE TRIGGER tr_log_meeting_t_motions_default_workflow_id AFTER INSERT OR UPDATE OF motions_default_workflow_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_workflow_id'); +CREATE TRIGGER tr_log_meeting_t_motions_default_amendment_workflow_id AFTER INSERT OR UPDATE OF motions_default_amendment_workflow_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_amendment_workflow_id'); +CREATE TRIGGER tr_log_meeting_t_logo_projector_main_id AFTER INSERT OR UPDATE OF logo_projector_main_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_main_id'); +CREATE TRIGGER tr_log_meeting_t_logo_projector_header_id AFTER INSERT OR UPDATE OF logo_projector_header_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_header_id'); +CREATE TRIGGER tr_log_meeting_t_logo_web_header_id AFTER INSERT OR UPDATE OF logo_web_header_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_web_header_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_l_id AFTER INSERT OR UPDATE OF logo_pdf_header_l_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_l_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_r_id AFTER INSERT OR UPDATE OF logo_pdf_header_r_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_r_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_l_id AFTER INSERT OR UPDATE OF logo_pdf_footer_l_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_l_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_r_id AFTER INSERT OR UPDATE OF logo_pdf_footer_r_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_r_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_ballot_paper_id AFTER INSERT OR UPDATE OF logo_pdf_ballot_paper_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_ballot_paper_id'); +CREATE TRIGGER tr_log_meeting_t_font_regular_id AFTER INSERT OR UPDATE OF font_regular_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_regular_id'); +CREATE TRIGGER tr_log_meeting_t_font_italic_id AFTER INSERT OR UPDATE OF font_italic_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_italic_id'); +CREATE TRIGGER tr_log_meeting_t_font_bold_id AFTER INSERT OR UPDATE OF font_bold_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_id'); +CREATE TRIGGER tr_log_meeting_t_font_bold_italic_id AFTER INSERT OR UPDATE OF font_bold_italic_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_italic_id'); +CREATE TRIGGER tr_log_meeting_t_font_monospace_id AFTER INSERT OR UPDATE OF font_monospace_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_monospace_id'); +CREATE TRIGGER tr_log_meeting_t_font_chyron_speaker_name_id AFTER INSERT OR UPDATE OF font_chyron_speaker_name_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_chyron_speaker_name_id'); +CREATE TRIGGER tr_log_meeting_t_font_projector_h1_id AFTER INSERT OR UPDATE OF font_projector_h1_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h1_id'); +CREATE TRIGGER tr_log_meeting_t_font_projector_h2_id AFTER INSERT OR UPDATE OF font_projector_h2_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h2_id'); +CREATE TRIGGER tr_log_meeting_t_committee_id AFTER INSERT OR UPDATE OF committee_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'committee_id'); + +CREATE TRIGGER tr_log_nm_meeting_present_user_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','meeting_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_meeting_t_reference_projector_id AFTER INSERT OR UPDATE OF reference_projector_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'reference_projector_id'); +CREATE TRIGGER tr_log_meeting_t_list_of_speakers_countdown_id AFTER INSERT OR UPDATE OF list_of_speakers_countdown_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'list_of_speakers_countdown_id'); +CREATE TRIGGER tr_log_meeting_t_poll_countdown_id AFTER INSERT OR UPDATE OF poll_countdown_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'poll_countdown_id'); +CREATE TRIGGER tr_log_meeting_t_default_group_id AFTER INSERT OR UPDATE OF default_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'default_group_id'); +CREATE TRIGGER tr_log_meeting_t_admin_group_id AFTER INSERT OR UPDATE OF admin_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'admin_group_id'); +CREATE TRIGGER tr_log_meeting_t_anonymous_group_id AFTER INSERT OR UPDATE OF anonymous_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'anonymous_group_id'); + +CREATE TRIGGER tr_log_structure_level AFTER INSERT OR UPDATE OR DELETE ON structure_level_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON group_t +CREATE TRIGGER tr_log_structure_level_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_group AFTER INSERT OR UPDATE OR DELETE ON group_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('group'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON personal_note_t + +CREATE TRIGGER tr_log_nm_group_meeting_user_ids_meeting_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_meeting_user_ids_meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','meeting_user','meeting_user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_meeting_user_ids_meeting_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_group_mmagi_meeting_mediafile_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_mmagi_meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','meeting_mediafile','meeting_mediafile_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_mmagi_meeting_mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_group_mmiagi_meeting_mediafile_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_mmiagi_meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','meeting_mediafile','meeting_mediafile_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_mmiagi_meeting_mediafile_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_group_read_comment_section_ids_motion_comment_section_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_read_comment_section_ids_motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','motion_comment_section','motion_comment_section_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_read_comment_section_ids_motion_comment_section_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_group_write_comment_section_ids_motion_comment_section_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_write_comment_section_ids_motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','motion_comment_section','motion_comment_section_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_write_comment_section_ids_motion_comment_section_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_group_poll_ids_poll_t AFTER INSERT OR UPDATE OR DELETE ON nm_group_poll_ids_poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group','group_id','poll','poll_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_group_poll_ids_poll_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_group_t_used_as_motion_poll_default_id AFTER INSERT OR UPDATE OF used_as_motion_poll_default_id OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_motion_poll_default_id'); +CREATE TRIGGER tr_log_group_t_used_as_assignment_poll_default_id AFTER INSERT OR UPDATE OF used_as_assignment_poll_default_id OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_assignment_poll_default_id'); +CREATE TRIGGER tr_log_group_t_used_as_topic_poll_default_id AFTER INSERT OR UPDATE OF used_as_topic_poll_default_id OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_topic_poll_default_id'); +CREATE TRIGGER tr_log_group_t_used_as_poll_default_id AFTER INSERT OR UPDATE OF used_as_poll_default_id OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_poll_default_id'); +CREATE TRIGGER tr_log_group_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_personal_note AFTER INSERT OR UPDATE OR DELETE ON personal_note_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('personal_note'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON personal_note_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON tag_t +CREATE TRIGGER tr_log_personal_note_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); +CREATE TRIGGER tr_log_personal_note_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_tag AFTER INSERT OR UPDATE OR DELETE ON tag_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('tag'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t + +CREATE TRIGGER tr_log_tagged_id_agenda_item_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_agenda_item_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','agenda_item','tagged_id_agenda_item_id'); + +CREATE TRIGGER tr_log_tagged_id_assignment_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_assignment_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','assignment','tagged_id_assignment_id'); + +CREATE TRIGGER tr_log_tagged_id_motion_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_motion_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','motion','tagged_id_motion_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_tag_tagged_ids_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_tag_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_agenda_item AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('agenda_item'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_motion_block_content_object_id_motion_block_id AFTER INSERT OR UPDATE OF content_object_id_motion_block_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block','content_object_id_motion_block_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); +CREATE TRIGGER tr_log_agenda_item_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('agenda_item', 'parent_id'); +CREATE TRIGGER tr_log_agenda_item_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_list_of_speakers AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('list_of_speakers'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_motion_block_content_object_id_motion_block_id AFTER INSERT OR UPDATE OF content_object_id_motion_block_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block','content_object_id_motion_block_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); + +CREATE TRIGGER tr_log_meeting_mediafile_content_object_id_meeting_mediafile_id AFTER INSERT OR UPDATE OF content_object_id_meeting_mediafile_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','content_object_id_meeting_mediafile_id'); +CREATE TRIGGER tr_log_list_of_speakers_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_structure_level_list_of_speakers AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level_list_of_speakers'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_structure_level_id AFTER INSERT OR UPDATE OF structure_level_id OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level', 'structure_level_id'); +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_point_of_order_category AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('point_of_order_category'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON speaker_t +CREATE TRIGGER tr_log_point_of_order_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON point_of_order_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_speaker AFTER INSERT OR UPDATE OR DELETE ON speaker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('speaker'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON speaker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON topic_t +CREATE TRIGGER tr_log_speaker_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); +CREATE TRIGGER tr_log_speaker_t_structure_level_list_of_speakers_id AFTER INSERT OR UPDATE OF structure_level_list_of_speakers_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level_list_of_speakers', 'structure_level_list_of_speakers_id'); +CREATE TRIGGER tr_log_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_speaker_t_point_of_order_category_id AFTER INSERT OR UPDATE OF point_of_order_category_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('point_of_order_category', 'point_of_order_category_id'); +CREATE TRIGGER tr_log_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_topic AFTER INSERT OR UPDATE OR DELETE ON topic_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('topic'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON topic_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_t +CREATE TRIGGER tr_log_topic_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON topic_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion AFTER INSERT OR UPDATE OR DELETE ON motion_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +CREATE TRIGGER tr_log_motion_t_lead_motion_id AFTER INSERT OR UPDATE OF lead_motion_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'lead_motion_id'); +CREATE TRIGGER tr_log_motion_t_sort_parent_id AFTER INSERT OR UPDATE OF sort_parent_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'sort_parent_id'); +CREATE TRIGGER tr_log_motion_t_origin_id AFTER INSERT OR UPDATE OF origin_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'origin_id'); +CREATE TRIGGER tr_log_motion_t_origin_meeting_id AFTER INSERT OR UPDATE OF origin_meeting_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'origin_meeting_id'); + +CREATE TRIGGER tr_log_nm_motion_all_derived_motion_ids_motion_t AFTER INSERT OR UPDATE OR DELETE ON nm_motion_all_derived_motion_ids_motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','all_derived_motion_id','motion','all_origin_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_motion_all_derived_motion_ids_motion_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_motion_identical_motion_ids_motion_t AFTER INSERT OR UPDATE OR DELETE ON nm_motion_identical_motion_ids_motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','identical_motion_id_1','motion','identical_motion_id_2'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_motion_identical_motion_ids_motion_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_motion_t_state_id AFTER INSERT OR UPDATE OF state_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'state_id'); +CREATE TRIGGER tr_log_motion_t_recommendation_id AFTER INSERT OR UPDATE OF recommendation_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'recommendation_id'); + +CREATE TRIGGER tr_log_state_extension_reference_id_motion_id_gm_motion_state_extension_reference_ids_t AFTER INSERT OR UPDATE OF state_extension_reference_id_motion_id OR DELETE ON gm_motion_state_extension_reference_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','motion_id','motion','state_extension_reference_id_motion_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_motion_state_extension_reference_ids_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_recommendation_extension_reference_id_motion_id_gm_motion_recommendation_extension_reference_ids_t AFTER INSERT OR UPDATE OF recommendation_extension_reference_id_motion_id OR DELETE ON gm_motion_recommendation_extension_reference_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','motion_id','motion','recommendation_extension_reference_id_motion_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_motion_recommendation_extension_reference_ids_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_motion_t_category_id AFTER INSERT OR UPDATE OF category_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_category', 'category_id'); +CREATE TRIGGER tr_log_motion_t_block_id AFTER INSERT OR UPDATE OF block_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block', 'block_id'); +CREATE TRIGGER tr_log_motion_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_submitter AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_submitter'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +CREATE TRIGGER tr_log_motion_submitter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_submitter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_submitter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_editor AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +CREATE TRIGGER tr_log_motion_editor_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_editor_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_editor_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_working_group_speaker AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_working_group_speaker'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t +CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_working_group_speaker_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_comment AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t +CREATE TRIGGER tr_log_motion_comment_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_comment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_comment_t_section_id AFTER INSERT OR UPDATE OF section_id OR DELETE ON motion_comment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_comment_section', 'section_id'); +CREATE TRIGGER tr_log_motion_comment_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_comment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_comment_section AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_comment_section'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_comment_section_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +CREATE TRIGGER tr_log_motion_comment_section_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_category AFTER INSERT OR UPDATE OR DELETE ON motion_category_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_category'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +CREATE TRIGGER tr_log_motion_category_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_category', 'parent_id'); +CREATE TRIGGER tr_log_motion_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_block AFTER INSERT OR UPDATE OR DELETE ON motion_block_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_block'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_block_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +CREATE TRIGGER tr_log_motion_block_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_change_recommendation AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_change_recommendation'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_state_t +CREATE TRIGGER tr_log_motion_change_recommendation_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_change_recommendation_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_change_recommendation_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_change_recommendation_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_state AFTER INSERT OR UPDATE OR DELETE ON motion_state_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_state'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_state_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t +CREATE TRIGGER tr_log_motion_state_t_submitter_withdraw_state_id AFTER INSERT OR UPDATE OF submitter_withdraw_state_id OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'submitter_withdraw_state_id'); + +CREATE TRIGGER tr_log_nm_motion_state_next_state_ids_motion_state_t AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state','next_state_id','motion_state','previous_state_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_motion_state_t_workflow_id AFTER INSERT OR UPDATE OF workflow_id OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'workflow_id'); +CREATE TRIGGER tr_log_motion_state_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_workflow AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_workflow'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_t +CREATE TRIGGER tr_log_motion_workflow_t_first_state_id AFTER INSERT OR UPDATE OF first_state_id OR DELETE ON motion_workflow_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'first_state_id'); +CREATE TRIGGER tr_log_motion_workflow_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_workflow_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_poll AFTER INSERT OR UPDATE OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON option_t + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); +CREATE TRIGGER tr_log_poll_t_global_option_id AFTER INSERT OR UPDATE OF global_option_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'global_option_id'); + +CREATE TRIGGER tr_log_nm_poll_voted_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','poll_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_option AFTER INSERT OR UPDATE OR DELETE ON option_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('option'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON option_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON vote_t +CREATE TRIGGER tr_log_option_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_user_content_object_id_user_id AFTER INSERT OR UPDATE OF content_object_id_user_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user','content_object_id_user_id'); + +CREATE TRIGGER tr_log_poll_candidate_list_content_object_id_poll_candidate_list_id AFTER INSERT OR UPDATE OF content_object_id_poll_candidate_list_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list','content_object_id_poll_candidate_list_id'); +CREATE TRIGGER tr_log_option_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_vote AFTER INSERT OR UPDATE OR DELETE ON vote_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_t +CREATE TRIGGER tr_log_vote_t_option_id AFTER INSERT OR UPDATE OF option_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'option_id'); +CREATE TRIGGER tr_log_vote_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_vote_t_delegated_user_id AFTER INSERT OR UPDATE OF delegated_user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'delegated_user_id'); +CREATE TRIGGER tr_log_vote_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +CREATE TRIGGER tr_log_assignment_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_assignment_candidate AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment_candidate'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +CREATE TRIGGER tr_log_assignment_candidate_t_assignment_id AFTER INSERT OR UPDATE OF assignment_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment', 'assignment_id'); +CREATE TRIGGER tr_log_assignment_candidate_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_assignment_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_poll_candidate_list AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +CREATE TRIGGER tr_log_poll_candidate_list_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_poll_candidate AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +CREATE TRIGGER tr_log_poll_candidate_t_poll_candidate_list_id AFTER INSERT OR UPDATE OF poll_candidate_list_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list', 'poll_candidate_list_id'); +CREATE TRIGGER tr_log_poll_candidate_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_poll_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_mediafile AFTER INSERT OR UPDATE OR DELETE ON mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +CREATE TRIGGER tr_log_mediafile_t_published_to_meetings_in_organization_id AFTER INSERT OR UPDATE OF published_to_meetings_in_organization_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'published_to_meetings_in_organization_id'); +CREATE TRIGGER tr_log_mediafile_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'parent_id'); + +CREATE TRIGGER tr_log_meeting_owner_id_meeting_id AFTER INSERT OR UPDATE OF owner_id_meeting_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','owner_id_meeting_id'); + +CREATE TRIGGER tr_log_organization_owner_id_organization_id AFTER INSERT OR UPDATE OF owner_id_organization_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization','owner_id_organization_id'); + +CREATE TRIGGER tr_log_meeting_mediafile AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_mediafile'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_t +CREATE TRIGGER tr_log_meeting_mediafile_t_mediafile_id AFTER INSERT OR UPDATE OF mediafile_id OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'mediafile_id'); +CREATE TRIGGER tr_log_meeting_mediafile_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_attachment_id_motion_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_motion_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','motion','attachment_id_motion_id'); + +CREATE TRIGGER tr_log_attachment_id_topic_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_topic_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','topic','attachment_id_topic_id'); + +CREATE TRIGGER tr_log_attachment_id_assignment_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_assignment_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','assignment','attachment_id_assignment_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_meeting_mediafile_attachment_ids_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_projector AFTER INSERT OR UPDATE OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projection_t +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_agenda_item_li AFTER INSERT OR UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_topic_in_meeti AFTER INSERT OR UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_topic_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_list_of_speake AFTER INSERT OR UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_current_los_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_current_los_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_in_meet AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_amendment_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_amendment_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_block_i AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_block_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_in_ AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_mediafile_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_mediafile_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_message_in_mee AFTER INSERT OR UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_message_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_countdown_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_countdown_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_pol AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_poll_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_poll_in_meetin AFTER INSERT OR UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_projection AFTER INSERT OR UPDATE OR DELETE ON projection_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projection'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projection_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +CREATE TRIGGER tr_log_projection_t_current_projector_id AFTER INSERT OR UPDATE OF current_projector_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'current_projector_id'); +CREATE TRIGGER tr_log_projection_t_preview_projector_id AFTER INSERT OR UPDATE OF preview_projector_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'preview_projector_id'); +CREATE TRIGGER tr_log_projection_t_history_projector_id AFTER INSERT OR UPDATE OF history_projector_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'history_projector_id'); + +CREATE TRIGGER tr_log_meeting_content_object_id_meeting_id AFTER INSERT OR UPDATE OF content_object_id_meeting_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','content_object_id_meeting_id'); + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_meeting_mediafile_content_object_id_meeting_mediafile_id AFTER INSERT OR UPDATE OF content_object_id_meeting_mediafile_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','content_object_id_meeting_mediafile_id'); + +CREATE TRIGGER tr_log_list_of_speakers_content_object_id_list_of_speakers_id AFTER INSERT OR UPDATE OF content_object_id_list_of_speakers_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers','content_object_id_list_of_speakers_id'); + +CREATE TRIGGER tr_log_motion_block_content_object_id_motion_block_id AFTER INSERT OR UPDATE OF content_object_id_motion_block_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block','content_object_id_motion_block_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_agenda_item_content_object_id_agenda_item_id AFTER INSERT OR UPDATE OF content_object_id_agenda_item_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('agenda_item','content_object_id_agenda_item_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); + +CREATE TRIGGER tr_log_poll_content_object_id_poll_id AFTER INSERT OR UPDATE OF content_object_id_poll_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','content_object_id_poll_id'); + +CREATE TRIGGER tr_log_projector_message_content_object_id_projector_message_id AFTER INSERT OR UPDATE OF content_object_id_projector_message_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_message','content_object_id_projector_message_id'); + +CREATE TRIGGER tr_log_projector_countdown_content_object_id_projector_countdown_id AFTER INSERT OR UPDATE OF content_object_id_projector_countdown_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown','content_object_id_projector_countdown_id'); +CREATE TRIGGER tr_log_projection_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_projector_message AFTER INSERT OR UPDATE OR DELETE ON projector_message_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_message'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_message_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t +CREATE TRIGGER tr_log_projector_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_projector_countdown AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_countdown'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +CREATE TRIGGER tr_log_projector_countdown_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_countdown_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_chat_group AFTER INSERT OR UPDATE OR DELETE ON chat_group_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_group'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON chat_message_t + +CREATE TRIGGER tr_log_nm_chat_group_read_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_nm_chat_group_write_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_chat_group_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_chat_message AFTER INSERT OR UPDATE OR DELETE ON chat_message_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_message'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_message_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +CREATE TRIGGER tr_log_chat_message_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_chat_message_t_chat_group_id AFTER INSERT OR UPDATE OF chat_group_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group', 'chat_group_id'); +CREATE TRIGGER tr_log_chat_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_action_worker AFTER INSERT OR UPDATE OR DELETE ON action_worker_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('action_worker'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON action_worker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +CREATE TRIGGER tr_log_import_preview AFTER INSERT OR UPDATE OR DELETE ON import_preview_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('import_preview'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON import_preview_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 747d6125..fa839b4f 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -120,6 +120,7 @@ def generate_the_code( for table_name, fields in MODELS.items(): if table_name in ["_migration_index", "_meta"]: continue + schema_zone_texts = cast(SchemaZoneTexts, defaultdict(str)) cls.intermediate_tables = {} @@ -325,6 +326,14 @@ def get_relation_type( initially_deferred, ) ) + table_name = HelperGetNames.get_table_name(table_name) + text["create_trigger_notify"] = Helper.get_foreign_key_notify_trigger( + table_name, + foreign_table_field.table, + fname, + foreign_table_field.ref_column, + initially_deferred, + ) elif state == FieldSqlErrorType.SQL: if sql := fdata.get("sql", ""): text["view"] = sql + ",\n" @@ -390,6 +399,12 @@ def get_relation_list_type( ) if nm_table_name not in cls.intermediate_tables: cls.intermediate_tables[nm_table_name] = value + text["create_trigger_notify"] = ( + Helper.get_trigger_for_intermediate_table( + own_table_field, + foreign_table_field, + ) + ) else: raise Exception( f"Tried to create im_table '{nm_table_name}' twice" @@ -424,16 +439,16 @@ def get_relation_list_type( ) else: own_ref_column = own_table_field.ref_column - foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" + foreign_table_ref_column = f"{foreign_table_field.view}_{foreign_table_field.ref_column}" foreign_table_name = HelperGetNames.get_nm_table_name( own_table_field, foreign_table_field ) foreign_table_column = ( - f"{own_table_field.table}_{own_table_field.ref_column}" + f"{own_table_field.view}_{own_table_field.ref_column}" ) elif type_ == "generic-relation-list": own_ref_column = own_table_field.ref_column - foreign_table_ref_column = f"{foreign_table_field.table}_{foreign_table_field.ref_column}" + foreign_table_ref_column = f"{foreign_table_field.view}_{foreign_table_field.ref_column}" foreign_table_name = HelperGetNames.get_gm_table_name( foreign_table_field ) @@ -562,6 +577,14 @@ def get_generic_relation_type( own_table_field.column, foreign_table_field, ) + text[ + "create_trigger_notify" + ] += Helper.get_trigger_for_generic_relation( + table_name, + generic_plain_field_name, + own_table_field.column, + foreign_table_field.table, + ) text[ "alter_table_final" ] += Helper.get_foreign_key_table_constraint_as_alter_table( @@ -598,6 +621,11 @@ def get_generic_relation_list_type( gm_foreign_table, value = Helper.get_gm_table_for_gm_nm_relation_lists( own_table_field, foreign_table_fields ) + text[ + "create_trigger_notify" + ] += Helper.get_trigger_for_generic_intermediate_table( + own_table_field, foreign_table_fields + ) if gm_foreign_table not in cls.intermediate_tables: cls.intermediate_tables[gm_foreign_table] = value else: @@ -688,17 +716,19 @@ class Helper: CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; - operation TEXT; - fqid TEXT; + operation_var TEXT; + fqid_var TEXT; BEGIN escaped_table_name := TG_ARGV[0]; - operation := LOWER(TG_OP); - fqid := escaped_table_name || '/' || NEW.id; + operation_var := LOWER(TG_OP); + fqid_var := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - fqid = escaped_table_name || '/' || OLD.id; + fqid_var = escaped_table_name || '/' || OLD.id; END IF; - INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) VALUES (operation, fqid, pg_current_xact_id(), 'now'); + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) + VALUES (operation_var, fqid_var, pg_current_xact_id(), 'now') + ON CONFLICT (operation,fqid,xact_id) DO NOTHING; RETURN NULL; -- AFTER TRIGGER needs no return END; $log_modified_trigger$ LANGUAGE plpgsql; @@ -728,12 +758,49 @@ class Helper: END; $notify_trigger$ LANGUAGE plpgsql; + CREATE OR REPLACE FUNCTION log_modified_related_models() + RETURNS trigger AS $log_modified_related_trigger$ + DECLARE + operation_var TEXT; + fqid_var TEXT; + ref_column TEXT; + foreign_table TEXT; + foreign_id TEXT; + i INTEGER := 0; + BEGIN + operation_var := LOWER(TG_OP); + + WHILE i < TG_NARGS LOOP + foreign_table := TG_ARGV[i]; + ref_column := TG_ARGV[i+1]; + + IF (TG_OP = 'DELETE') THEN + EXECUTE format('SELECT ($1).%I', ref_column) INTO foreign_id USING OLD; + ELSE + EXECUTE format('SELECT ($1).%I', ref_column) INTO foreign_id USING NEW; + END IF; + + IF foreign_id IS NOT NULL THEN + fqid_var := foreign_table || '/' || foreign_id; + INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) + VALUES (operation_var, fqid_var, pg_current_xact_id(), now()) + ON CONFLICT (operation,fqid,xact_id) DO NOTHING; + END IF; + + i := i + 2; + END LOOP; + + RETURN NULL; -- AFTER TRIGGER needs no return + END; + $log_modified_related_trigger$ LANGUAGE plpgsql; + CREATE TABLE os_notify_log_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, operation varchar(32), - fqid varchar(256), + fqid varchar(256) NOT NULL, xact_id xid8, - timestamp timestamptz + timestamp timestamptz, + CONSTRAINT unique_fqid_xact_id_operation UNIQUE (operation,fqid,xact_id) ); """ ) @@ -838,9 +905,10 @@ def get_view_body_end(table_name: str, code: str) -> str: @staticmethod def get_notify_trigger(table_name: str) -> str: + trigger_name = HelperGetNames.get_notify_trigger_name(table_name) own_table = HelperGetNames.get_table_name(table_name) escaped_table_name = "'" + table_name + "'" - code = f"CREATE TRIGGER log_modified_model AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" + code = f"CREATE TRIGGER {trigger_name} AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" code += f"FOR EACH ROW EXECUTE FUNCTION log_modified_models({escaped_table_name});\n" code += f"CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON {own_table}\n" code += "DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end();\n" @@ -922,6 +990,23 @@ def get_on_action_mode(action: str, delete: bool) -> str: raise Exception(f"{action} is not a valid action mode") return "" + @staticmethod + def get_foreign_key_notify_trigger( + table_name: str, + foreign_table: str, + ref_column: str, + fk_columns: list[str] | str, + initially_deferred: bool = False, + delete_action: str = "", + update_action: str = "", + ) -> str: + trigger_name = HelperGetNames.get_notify_related_trigger_name( + table_name, ref_column + ) + own_table = HelperGetNames.get_table_name(table_name) + return f"""CREATE TRIGGER {trigger_name} AFTER INSERT OR UPDATE OF {ref_column} OR DELETE ON {own_table} +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('{foreign_table}', '{ref_column}');\n""" + @staticmethod def get_nm_table_for_n_m_relation_lists( own_table_field: TableFieldType, foreign_table_field: TableFieldType @@ -938,9 +1023,10 @@ def get_nm_table_for_n_m_relation_lists( if field1 == field2: field1 += "_1" field2 += "_2" + table_name = HelperGetNames.get_table_name(nm_table_name) text = Helper.INTERMEDIATE_TABLE_N_M_RELATION_TEMPLATE.substitute( { - "table_name": nm_table_name, # without tailing _t + "table_name": table_name, "field1": field1, "table1": HelperGetNames.get_table_name(own_table_field.table), "field2": field2, @@ -983,7 +1069,7 @@ def get_gm_table_for_gm_nm_relation_lists( text = Helper.INTERMEDIATE_TABLE_G_M_RELATION_TEMPLATE.substitute( { - "table_name": gm_table_name, # without trailing _t + "table_name": gm_table_name, "own_table_name": HelperGetNames.get_table_name(own_table_field.table), "own_table_name_with_ref_column": ( own_table_name_with_ref_column := f"{own_table_field.table}_{own_table_field.ref_column}" @@ -1002,6 +1088,73 @@ def get_gm_table_for_gm_nm_relation_lists( ) return gm_table_name, text + @staticmethod + def get_trigger_for_intermediate_table( + own_table_field: TableFieldType, foreign_table_field: TableFieldType + ) -> str: + + field1 = HelperGetNames.get_field_in_n_m_relation_list( + own_table_field, foreign_table_field.table + ) + field2 = HelperGetNames.get_field_in_n_m_relation_list( + foreign_table_field, own_table_field.table + ) + if field1 == field2: + field1 += "_1" + field2 += "_2" + nm_table_name = HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ) + + table_name = HelperGetNames.get_table_name(nm_table_name) + trigger_name = f"tr_log_{table_name}" + + return f""" +CREATE TRIGGER {trigger_name} AFTER INSERT OR UPDATE OR DELETE ON {nm_table_name} +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('{own_table_field.table}','{field1}','{foreign_table_field.table}','{field2}'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON {nm_table_name} +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +""" + + @staticmethod + def get_trigger_for_generic_relation( + table_name: str, + generic_plain_field_name: str, + own_column: str, + foreign_table: str, + ) -> str: + trigger_name = f"tr_log_{foreign_table}_{generic_plain_field_name}" + own_table_name = HelperGetNames.get_table_name(table_name) + return f""" +CREATE TRIGGER {trigger_name} AFTER INSERT OR UPDATE OF {generic_plain_field_name} OR DELETE ON {own_table_name} +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('{foreign_table}','{generic_plain_field_name}'); +""" + + @staticmethod + def get_trigger_for_generic_intermediate_table( + own_table_field: TableFieldType, foreign_table_fields: list[TableFieldType] + ) -> str: + + gm_table_name = HelperGetNames.get_gm_table_name(own_table_field) + trigger_text = "" + + for foreign_table_field in foreign_table_fields: + gm_content_field = HelperGetNames.get_gm_content_field( + own_table_field.intermediate_column, foreign_table_field.table + ) + trigger_name = f"tr_log_{gm_content_field}_{gm_table_name}" + own_table_name_with_ref_column = ( + f"{own_table_field.table}_{own_table_field.ref_column}" + ) + trigger_text += f""" +CREATE TRIGGER {trigger_name} AFTER INSERT OR UPDATE OF {gm_content_field} OR DELETE ON {gm_table_name} +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('{own_table_field.table}','{own_table_name_with_ref_column}','{foreign_table_field.table}','{gm_content_field}'); +""" + trigger_text += f"""CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON {gm_table_name} +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +""" + return trigger_text + @staticmethod def get_initials( table_name: str, fname: str, type_: str, fdata: dict[str, Any] diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 05214ca4..1b1a7f73 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -20,6 +20,7 @@ def __init__( ref_column: str = "id", ): self.table = table + self.view = table.rstrip("_t") self.column = column self.intermediate_column = column[:-1] self.field_def: dict[str, Any] = field_def or {} @@ -91,7 +92,10 @@ def get_initial_letters(words: str) -> str: @max_length def get_table_name(table_name: str) -> str: """get's the table name as old collection name with appendix '_t'""" - return table_name + "_t" + if not table_name.endswith("_t"): + return table_name + "_t" + else: + return table_name @staticmethod @max_length @@ -104,15 +108,15 @@ def get_view_name(table_name: str) -> str: def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: """get's the table name n:m-relations intermediate table""" if (f"{own.table}_{own.column}") < (f"{foreign.table}_{foreign.column}"): - return f"nm_{own.table}_{HelperGetNames.get_initial_letters(own.column)}_{foreign.table}" + return f"nm_{own.table}_{HelperGetNames.get_initial_letters(own.column)}_{foreign.table}_t" else: - return f"nm_{foreign.table}_{HelperGetNames.get_initial_letters(foreign.column)}_{own.table}" + return f"nm_{foreign.table}_{HelperGetNames.get_initial_letters(foreign.column)}_{own.table}_t" @staticmethod @max_length def get_gm_table_name(table_field: TableFieldType) -> str: """get's th table name for generic-list:many-relations intermediate table""" - return f"gm_{table_field.table}_{table_field.column}" + return f"gm_{table_field.table}_{table_field.column}_t" @staticmethod @max_length @@ -205,6 +209,31 @@ def get_not_null_rel_list_upd_del_trigger_name( HelperGetNames.trigger_unique_list.append(name) return name + @staticmethod + @max_length + def get_notify_trigger_name( + table_name: str, + ) -> str: + """gets the name of the trigger for logging changes on models""" + name = f"tr_log_{table_name}"[: HelperGetNames.MAX_LEN] + if name in HelperGetNames.trigger_unique_list: + raise Exception(f"trigger {name} is not unique!") + HelperGetNames.trigger_unique_list.append(name) + return name + + @staticmethod + @max_length + def get_notify_related_trigger_name( + table_name: str, + column_name: str, + ) -> str: + """gets the name of the trigger for logging changes on related models""" + name = f"tr_log_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] + if name in HelperGetNames.trigger_unique_list: + raise Exception(f"trigger {name} is not unique!") + HelperGetNames.trigger_unique_list.append(name) + return name + class InternalHelper: MODELS: dict[str, dict[str, Any]] = {} diff --git a/models.yml b/models.yml index 24cb2189..af65efd1 100644 --- a/models.yml +++ b/models.yml @@ -406,7 +406,7 @@ user: -- Select committee_ids from meetings the user is part of SELECT m.committee_id FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id INNER JOIN meeting_t AS m ON m.id = mu.meeting_id WHERE mu.user_id = u.id @@ -414,7 +414,7 @@ user: -- Select committee_ids from committee managers SELECT cmu.committee_id - FROM nm_committee_manager_ids_user cmu + FROM nm_committee_manager_ids_user_t cmu WHERE cmu.user_id = u.id UNION @@ -470,7 +470,7 @@ user: ( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids to: meeting/user_ids @@ -824,14 +824,14 @@ committee: SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id UNION -- Select user_ids from committee managers SELECT cmu.user_id - FROM nm_committee_manager_ids_user cmu + FROM nm_committee_manager_ids_user_t cmu WHERE cmu.committee_id = c.id UNION @@ -1939,7 +1939,7 @@ meeting: ( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids to: user/meeting_ids From 39e3d28a92f7932061c7e1d9750d5a185c02fba7 Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:50:06 +0200 Subject: [PATCH 099/142] [rel-db] Handle relational fields referencing themselves (#293) --- dev/sql/schema_relational.sql | 33 +++++++++++++++++ dev/src/generate_sql_schema.py | 67 +++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 26098aff..c7bf4b0f 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -79,6 +79,30 @@ BEGIN END; $log_modified_trigger$ LANGUAGE plpgsql; +CREATE FUNCTION check_unique_ids_pair() +RETURNS trigger +AS $unique_ids_pair_trigger$ +-- usage with 1 parameter IN TRIGGER DEFINITION: +-- base_column_name: name of write fields before adding numeric suffixes +-- Guards against mirrored duplicates by skipping one of the pairs. +DECLARE + base_column_name text; + value_1 integer; + value_2 integer; +BEGIN + base_column_name = TG_ARGV[0]; + value_1 := hstore(NEW) -> (base_column_name || '_1'); + value_2 := hstore(NEW) -> (base_column_name || '_2'); + + IF (value_1 > value_2) THEN + RETURN NULL; + END IF; + + RETURN NEW; +END; +$unique_ids_pair_trigger$ +LANGUAGE plpgsql; + CREATE FUNCTION notify_transaction_end() RETURNS trigger AS $notify_trigger$ DECLARE payload TEXT; @@ -2158,6 +2182,15 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'defa +-- Create triggers preventing mirrored duplicates in fields referencing themselves + +-- definition trigger unique ids pair for motion.identical_motion_ids +CREATE TRIGGER restrict_motion_identical_motion_ids BEFORE INSERT OR UPDATE ON nm_motion_identical_motion_ids_motion_t +FOR EACH ROW EXECUTE FUNCTION check_unique_ids_pair('identical_motion_id'); + + + + -- Create triggers for notify CREATE TRIGGER tr_log_organization AFTER INSERT OR UPDATE OR DELETE ON organization_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index fa839b4f..a38dd684 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -31,6 +31,7 @@ class SchemaZoneTexts(TypedDict, total=False): alter_table: str alter_table_final: str create_trigger_relationlistnotnull: str + create_trigger_unique_ids_pair_code: str create_trigger_notify: str undecided: str final_info: str @@ -70,7 +71,7 @@ class GenerateCodeBlocks: @classmethod def generate_the_code( cls, - ) -> tuple[str, str, str, str, str, list[str], str, str, str, list[str]]: + ) -> tuple[str, str, str, str, str, list[str], str, str, str, str, list[str]]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -83,6 +84,7 @@ def generate_the_code( n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field create_trigger_relationlistnotnull_code: Definitions of triggers calling check_not_null_for_relation_lists + create_trigger_unique_ids_pair_code: Definitions of triggers calling check_unique_ids_pair create_trigger_notify_code: Definitions of triggers calling notify_modified_models errors: to show """ @@ -111,6 +113,7 @@ def generate_the_code( view_name_code: str = "" alter_table_final_code: str = "" create_trigger_relationlistnotnull_code: str = "" + create_trigger_unique_ids_pair_code: str = "" create_trigger_notify_code: str = "" final_info_code: str = "" missing_handled_attributes = [] @@ -160,6 +163,8 @@ def generate_the_code( alter_table_final_code += code + "\n" if code := schema_zone_texts["create_trigger_relationlistnotnull"]: create_trigger_relationlistnotnull_code += code + "\n" + if code := schema_zone_texts["create_trigger_unique_ids_pair_code"]: + create_trigger_unique_ids_pair_code += code + "\n" if code := schema_zone_texts["final_info"]: final_info_code += code + "\n" for im_table in cls.intermediate_tables.values(): @@ -185,6 +190,7 @@ def generate_the_code( missing_handled_attributes, im_table_code, create_trigger_relationlistnotnull_code, + create_trigger_unique_ids_pair_code, create_trigger_notify_code, errors, ) @@ -484,6 +490,19 @@ def get_relation_list_type( foreign_table_field.column, ) ) + if ( + own_table_field.table == foreign_table_field.table + and own_table_field.column == foreign_table_field.column + ): + text["create_trigger_unique_ids_pair_code"] = ( + cls.get_trigger_check_unique_ids_pair( + own_table_field.table, + own_table_field.column, + HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ), + ) + ) if comment := fdata.get("description"): text["post_view"] += Helper.get_post_view_comment( HelperGetNames.get_view_name(table_name), fname, comment @@ -542,6 +561,23 @@ def get_trigger_check_not_null_for_relation_lists( """ ) + @classmethod + def get_trigger_check_unique_ids_pair( + cls, + view: str, + column: str, + table_name: str, + ) -> str: + base_column_name = column[:-1] + return dedent( + f""" + -- definition trigger unique ids pair for {view}.{column} + CREATE TRIGGER restrict_{view}_{column} BEFORE INSERT OR UPDATE ON {table_name} + FOR EACH ROW EXECUTE FUNCTION check_unique_ids_pair('{base_column_name}'); + + """ + ) + @classmethod def get_generic_relation_type( cls, table_name: str, fname: str, fdata: dict[str, Any], type_: str @@ -733,6 +769,30 @@ class Helper: END; $log_modified_trigger$ LANGUAGE plpgsql; + CREATE FUNCTION check_unique_ids_pair() + RETURNS trigger + AS $unique_ids_pair_trigger$ + -- usage with 1 parameter IN TRIGGER DEFINITION: + -- base_column_name: name of write fields before adding numeric suffixes + -- Guards against mirrored duplicates by skipping one of the pairs. + DECLARE + base_column_name text; + value_1 integer; + value_2 integer; + BEGIN + base_column_name = TG_ARGV[0]; + value_1 := hstore(NEW) -> (base_column_name || '_1'); + value_2 := hstore(NEW) -> (base_column_name || '_2'); + + IF (value_1 > value_2) THEN + RETURN NULL; + END IF; + + RETURN NEW; + END; + $unique_ids_pair_trigger$ + LANGUAGE plpgsql; + CREATE FUNCTION notify_transaction_end() RETURNS trigger AS $notify_trigger$ DECLARE payload TEXT; @@ -1370,6 +1430,7 @@ def main() -> None: missing_handled_attributes, im_table_code, create_trigger_relationlistnotnull_code, + create_trigger_unique_ids_pair_code, create_trigger_notify_code, errors, ) = GenerateCodeBlocks.generate_the_code() @@ -1394,6 +1455,10 @@ def main() -> None: "\n\n-- Create triggers checking foreign_id not null for relation-lists\n" ) dest.write(create_trigger_relationlistnotnull_code) + dest.write( + "\n\n-- Create triggers preventing mirrored duplicates in fields referencing themselves\n" + ) + dest.write(create_trigger_unique_ids_pair_code) dest.write("\n\n-- Create triggers for notify\n") dest.write(create_trigger_notify_code) dest.write(Helper.RELATION_LIST_AGENDA) From 10a6f4e6f30a011e7cb2387add4b87354abc4fda Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 25 Aug 2025 09:34:22 +0200 Subject: [PATCH 100/142] Move analog back to visibility --- dev/sql/schema_relational.sql | 6 +++--- models.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 9b576f57..76ae12de 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '064af14d230cc3641cfd64ef056e7f87' +-- MODELS_YML_CHECKSUM = '4093b008e1af5ee3c4254200f6272c5d' -- Database parameters @@ -914,9 +914,9 @@ CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, description text, - method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('analog', 'motion', 'selection', 'rating', 'single_transferable_vote')), + method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('motion', 'selection', 'rating', 'single_transferable_vote')), config jsonb, - visibility varchar(256) CONSTRAINT enum_poll_visibility CHECK (visibility IN ('named', 'open', 'secret')), + visibility varchar(256) CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', result jsonb NOT NULL, sequential_number integer NOT NULL, diff --git a/models.yml b/models.yml index 9a6fcaff..16601d34 100644 --- a/models.yml +++ b/models.yml @@ -3400,7 +3400,6 @@ poll: type: string required: true enum: - - analog - motion - selection - rating @@ -3413,6 +3412,7 @@ poll: visibility: type: string enum: + - manually - named - open - secret From 41c15d0122ae9e4d55bc1606584710604ff464b8 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 25 Aug 2025 12:36:31 +0200 Subject: [PATCH 101/142] poll/result is not required --- models.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/models.yml b/models.yml index 16601d34..259615ee 100644 --- a/models.yml +++ b/models.yml @@ -3429,7 +3429,6 @@ poll: result: type: JSON description: Calculated result. The format depends on the value in poll/method - required: true restriction_mode: B sequential_number: type: number From 076ab670ce552d711b2044cb7d5ba077b3f3cc32 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 25 Aug 2025 12:41:58 +0200 Subject: [PATCH 102/142] Update sql --- dev/sql/schema_relational.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 76ae12de..d81e0d8a 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '4093b008e1af5ee3c4254200f6272c5d' +-- MODELS_YML_CHECKSUM = '01253fa5beea1f850a6806f9b85e26e8' -- Database parameters @@ -918,7 +918,7 @@ CREATE TABLE poll_t ( config jsonb, visibility varchar(256) CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', - result jsonb NOT NULL, + result jsonb, sequential_number integer NOT NULL, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, From 41c23bb1cfaab0b2a87ae27efd6410524c4898ec Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 29 Aug 2025 16:56:09 +0200 Subject: [PATCH 103/142] Vote can be any text --- dev/sql/schema_relational.sql | 4 ++-- models.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index d81e0d8a..ccb76056 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '01253fa5beea1f850a6806f9b85e26e8' +-- MODELS_YML_CHECKSUM = '476d754a4a57fbf2dee239b64ed0610c' -- Database parameters @@ -938,7 +938,7 @@ comment on column poll_t.sequential_number is 'The (positive) serial number of t CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight decimal(16,6) DEFAULT '1.000000', - value jsonb, + value text, poll_id integer NOT NULL, acting_user_id integer, represented_user_id integer, diff --git a/models.yml b/models.yml index 259615ee..05e13810 100644 --- a/models.yml +++ b/models.yml @@ -3490,7 +3490,7 @@ vote: constant: true default: "1.000000" value: - type: JSON + type: text restriction_mode: A constant: true poll_id: From a26b45d33bb34d77f158fd330f0a3fe4d9272307 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 4 Sep 2025 09:47:27 +0200 Subject: [PATCH 104/142] [rel-db] Remove option from base_data (#305) * add test_data.sql * rework github bash file * sqlfluff integration --------- Co-authored-by: Hannes Janott --- .github/workflows/test-sql.yml | 14 +- dev/.sqlfluffignore | 1 + dev/Makefile | 11 +- dev/requirements.txt | 1 + .../{apply_base_data.sh => apply_data.sh} | 2 +- dev/setup.cfg | 10 +- dev/sql/base_data.sql | 265 ++++++++---------- dev/sql/test_data.sql | 76 +++++ dev/src/generate_sql_schema.py | 4 +- 9 files changed, 228 insertions(+), 156 deletions(-) create mode 100644 dev/.sqlfluffignore rename dev/scripts/{apply_base_data.sh => apply_data.sh} (59%) create mode 100644 dev/sql/test_data.sql diff --git a/.github/workflows/test-sql.yml b/.github/workflows/test-sql.yml index e7641ea5..608a50aa 100644 --- a/.github/workflows/test-sql.yml +++ b/.github/workflows/test-sql.yml @@ -38,9 +38,19 @@ jobs: DATABASE_NAME: openslides PGPASSWORD: password - - name: insert init data + - name: insert base data shell: bash - run: bash dev/scripts/apply_base_data.sh + run: bash dev/scripts/apply_data.sh base_data.sql + env: + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_USER: openslides + DATABASE_NAME: openslides + PGPASSWORD: password + + - name: insert test data + shell: bash + run: bash dev/scripts/apply_data.sh test_data.sql env: DATABASE_HOST: postgres DATABASE_PORT: 5432 diff --git a/dev/.sqlfluffignore b/dev/.sqlfluffignore new file mode 100644 index 00000000..01bb95cc --- /dev/null +++ b/dev/.sqlfluffignore @@ -0,0 +1 @@ +schema_relational.sql diff --git a/dev/Makefile b/dev/Makefile index 7670ec8f..c8f120f4 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -1,7 +1,7 @@ # Commands inside the container paths = src/ tests/ -all: pyupgrade black autoflake isort flake8 mypy +all: pyupgrade black autoflake isort flake8 mypy sqlfluff pyupgrade: pyupgrade --py310-plus --exit-zero-even-if-changed $$(find . -name '*.py') @@ -30,6 +30,9 @@ flake8: mypy: mypy src/ +sqlfluff: + sqlfluff fix --dialect postgres --verbose + validate-models: python src/validate.py @@ -45,8 +48,14 @@ create-database: apply-db-schema: scripts/apply_db_schema.sh +apply-test-data: + scripts/apply_data.sh base_data.sql + scripts/apply_data.sh test_data.sql + create-database-with-schema: drop-database create-database apply-db-schema +create-test-data: create-database-with-schema apply-test-data + run-psql: psql -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} -d ${DATABASE_NAME} diff --git a/dev/requirements.txt b/dev/requirements.txt index 63aad93f..b07b7d04 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -3,6 +3,7 @@ black==25.1.0 flake8==7.3.0 isort==6.0.1 mypy==1.17.1 +sqlfluff==3.4.2 pytest==8.4.1 pyupgrade==3.20.0 pyyaml==6.0.2 diff --git a/dev/scripts/apply_base_data.sh b/dev/scripts/apply_data.sh similarity index 59% rename from dev/scripts/apply_base_data.sh rename to dev/scripts/apply_data.sh index 442a4c9d..a49829a6 100755 --- a/dev/scripts/apply_base_data.sh +++ b/dev/scripts/apply_data.sh @@ -1,4 +1,4 @@ #!/bin/bash cd "$(dirname "$0")" -psql -1 -v ON_ERROR_STOP=1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/base_data.sql +psql -1 -v ON_ERROR_STOP=1 -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_NAME" -f ../sql/$1 diff --git a/dev/setup.cfg b/dev/setup.cfg index e27cfd7a..9650f784 100644 --- a/dev/setup.cfg +++ b/dev/setup.cfg @@ -16,4 +16,12 @@ line_length = 88 extend-ignore = E203,E501 [mypy] -disallow_untyped_defs = true \ No newline at end of file +disallow_untyped_defs = true + +[sqlfluff] +dialect = postgres +large_file_skip_byte_limit = 0 +processes = 0 + +[sqlfluff:rules:capitalisation.identifiers] +ignore_words = WARNING, diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index 892dc306..b18e1e83 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -1,167 +1,132 @@ -- This script can only be used for an empty database without used sequences. -INSERT INTO gender_t (name) VALUES('female'); -INSERT INTO user_t (username,gender_id) VALUES('tom',1); --relation --relation-list gender_ids -BEGIN; - INSERT INTO motion_state_t (id,name,weight,workflow_id,meeting_id) - VALUES (1,'motionState1',1,1,1); - INSERT INTO motion_workflow_t (id, name, sequential_number, first_state_id, meeting_id) - VALUES (1,'workflow1',1,1,1); - INSERT INTO meeting_t (id,name,motions_default_workflow_id,motions_default_amendment_workflow_id,committee_id,reference_projector_id,default_group_id) - VALUES (1,'name',1,1,1,1,1); - INSERT INTO organization_tag_t(id,name,color) - VALUES(1,'tagA','#cc3b03'); --generic-relation-list tagged_ids - Insert Into committee(id, name, default_meeting_id) - Values(1,'plenum',1); --relation-list organization_tag_ids --relation 1:1 default_meeting_id - INSERT INTO projector_t (id,sequential_number,meeting_id) - VALUES (1,1,1); - INSERT INTO group_t (id,name,meeting_id) - VALUES (1,'gruppe1',1); -COMMIT; - -INSERT INTO organization_tag_t (id,name,color) - VALUES (2,'bunt','#ffffff'); -INSERT INTO gm_organization_tag_tagged_ids_t(organization_tag_id,tagged_id) - VALUES(2,'meeting/1'); - -INSERT INTO topic_t (id,title, sequential_number, meeting_id) - VALUES (1, 'Thema3', 1, 1); -INSERT INTO agenda_item_t (content_object_id, meeting_id) - VALUES ('topic/1',1);--agenda_item.content_object_id:topic.agenda_item_id gr:r - -INSERT INTO option_t (id,content_object_id,meeting_id) - VALUES (1, 'user/1', 1);--rl:gr user.option_id:option.content_object_id - -INSERT INTO nm_committee_manager_ids_user_t - VALUES (1,1); --rl:rl committee_ids:user_ids - INSERT INTO - theme_t (id, name) +theme_t (id, name) VALUES - (1, 'standard theme'); +(1, 'standard theme'); INSERT INTO - organization_t (name, theme_id) +organization_t (name, theme_id) VALUES - ('Intevation', 1); +('Intevation', 1); INSERT INTO - committee_t (id, name) +committee_t (id, name) VALUES - (2, 'committee'); +(1, 'committee'); BEGIN; - INSERT INTO - meeting_t ( - id, - default_group_id, - admin_group_id, - motions_default_workflow_id, - motions_default_amendment_workflow_id, - committee_id, - reference_projector_id, - name - ) - VALUES - (2, 2, 2, 2, 2, 1, 2, 'meeting'); +INSERT INTO +meeting_t ( + id, + default_group_id, + admin_group_id, + motions_default_workflow_id, + motions_default_amendment_workflow_id, + committee_id, + reference_projector_id, + name +) +VALUES +(1, 1, 2, 1, 1, 1, 1, 'meeting'); - INSERT INTO - group_t (id, name, meeting_id, permissions) - VALUES - ( - 2, - 'Default', - 1, - '{ - "agenda_item.can_see", - "assignment.can_see", - "meeting.can_see_autopilot", - "meeting.can_see_frontpage", - "motion.can_see", - "projector.can_see" - }' - ), - (3, 'Admin', 1, DEFAULT); +INSERT INTO +group_t (id, name, meeting_id, permissions) +VALUES +( + 1, + 'Default', + 1, + '{ + "agenda_item.can_see", + "assignment.can_see", + "meeting.can_see_autopilot", + "meeting.can_see_frontpage", + "motion.can_see", + "projector.can_see" +}' +), +(2, 'Admin', 1, DEFAULT); - INSERT INTO - motion_workflow_t ( - id, - name, - sequential_number, - first_state_id, - meeting_id - ) - VALUES - (2, 'Simple Workflow', 1, 2, 2); +INSERT INTO +motion_workflow_t ( + id, + name, + sequential_number, + first_state_id, + meeting_id +) +VALUES +(1, 'Simple Workflow', 1, 1, 1); - INSERT INTO - motion_state_t ( - id, - name, - weight, - workflow_id, - meeting_id, - allow_create_poll, - allow_support, - set_workflow_timestamp, - recommendation_label, - css_class, - merge_amendment_into_final - ) - VALUES - ( - 2, - 'submitted', - 1, - 1, - 1, - true, - true, - true, - 'Submitted', - 'grey', - 'do_not_merge' - ), - ( - 3, - 'accepted', - 2, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'Acceptance', - 'green', - 'do_merge' - ), - ( - 4, - 'rejected', - 3, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'Rejection', - 'red', - 'do_not_merge' - ), - ( - 5, - 'not decided', - 4, - 1, - 1, - DEFAULT, - DEFAULT, - DEFAULT, - 'No decision', - 'grey', - 'do_not_merge' - ); +INSERT INTO +motion_state_t ( + id, + name, + weight, + workflow_id, + meeting_id, + allow_create_poll, + allow_support, + set_workflow_timestamp, + recommendation_label, + css_class, + merge_amendment_into_final +) +VALUES +( + 1, + 'submitted', + 1, + 1, + 1, + true, + true, + true, + 'Submitted', + 'grey', + 'do_not_merge' +), +( + 2, + 'accepted', + 2, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Acceptance', + 'green', + 'do_merge' +), +( + 3, + 'rejected', + 3, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'Rejection', + 'red', + 'do_not_merge' +), +( + 4, + 'not decided', + 4, + 1, + 1, + DEFAULT, + DEFAULT, + DEFAULT, + 'No decision', + 'grey', + 'do_not_merge' +); - INSERT INTO projector_t (id,sequential_number,meeting_id) - VALUES (2,2,1); +INSERT INTO projector_t (id, sequential_number, meeting_id) +VALUES (1, 1, 1); COMMIT; diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql new file mode 100644 index 00000000..98e2e21d --- /dev/null +++ b/dev/sql/test_data.sql @@ -0,0 +1,76 @@ +-- This script should only be used for an database filled with base_data.sql. + +--relation --relation-list gender_ids +INSERT INTO gender_t (name) VALUES ('female'); +INSERT INTO user_t (username, gender_id) VALUES ('tom', 1); + +BEGIN; + +INSERT INTO motion_state_t (id, name, weight, workflow_id, meeting_id) +VALUES (5, 'motionState5', 1, 2, 2); + +INSERT INTO motion_workflow_t ( + id, name, sequential_number, first_state_id, meeting_id +) +VALUES (2, 'workflow2', 2, 4, 2); + +INSERT INTO meeting_t ( + id, + name, + motions_default_workflow_id, + motions_default_amendment_workflow_id, + committee_id, + reference_projector_id, + default_group_id +) + +VALUES (2, 'name', 2, 2, 2, 2, 3); +INSERT INTO organization_tag_t (id, name, color) + +--generic-relation-list tagged_ids +VALUES (1, 'tagA', '#cc3b03'); + +--relation-list organization_tag_ids --relation 1:1 default_meeting_id +INSERT INTO committee (id, name, default_meeting_id) +VALUES (2, 'plenum', 2); + +INSERT INTO projector_t (id, sequential_number, meeting_id) +VALUES (2, 2, 2); + +INSERT INTO group_t (id, name, meeting_id) +VALUES (3, 'gruppe3', 2); + +COMMIT; + +INSERT INTO organization_tag_t (id, name, color) +VALUES (2, 'bunt', '#ffffff'); + +INSERT INTO gm_organization_tag_tagged_ids_t (organization_tag_id, tagged_id) +VALUES (2, 'meeting/1'); + +INSERT INTO topic_t (id, title, sequential_number, meeting_id) +VALUES (1, 'Thema1', 1, 2); + +--agenda_item.content_object_id:topic.agenda_item_id gr:r +INSERT INTO agenda_item_t (content_object_id, meeting_id) +VALUES ('topic/1', 2); + +--rl:gr topic.poll_ids:poll.content_object_id +INSERT INTO poll_t ( + id, + title, + type, + backend, + pollmethod, + onehundred_percent_base, + sequential_number, + content_object_id, + meeting_id +) +VALUES (1, 'Titel1', 'analog', 'fast', 'YNA', 'disabled', 1, 'topic/1', 2); + +--rl:rl committee_ids:user_ids +INSERT INTO nm_committee_manager_ids_user_t (committee_id, user_id) +VALUES (1, 1); + +COMMIT; diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index a38dd684..92b90808 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -9,6 +9,8 @@ from textwrap import dedent from typing import Any, TypedDict, cast +from sqlfluff import fix + from helper_get_names import FieldSqlErrorType # type: ignore from helper_get_names import ( KEYSEPARATOR, @@ -341,7 +343,7 @@ def get_relation_type( initially_deferred, ) elif state == FieldSqlErrorType.SQL: - if sql := fdata.get("sql", ""): + if sql := fix(fdata.get("sql", "")): text["view"] = sql + ",\n" elif foreign_table_field.field_def["type"] == "generic-relation": text["view"] = cls.get_sql_for_relation_1_1( From cbe528f080d855b9d25b7ff12edf9126242458b3 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 7 Sep 2025 15:22:43 +0200 Subject: [PATCH 105/142] make poll/config and poll/request text fields --- dev/sql/schema_relational.sql | 6 +++--- models.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index b532c0c0..c94b9514 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'd4572548fd951b4cc8303d8d26e68e64' +-- MODELS_YML_CHECKSUM = '06ffcb50123dd1f730ade66d92b05580' -- Database parameters @@ -916,10 +916,10 @@ CREATE TABLE poll_t ( title varchar(256) NOT NULL, description text, method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('motion', 'selection', 'rating', 'single_transferable_vote')), - config jsonb, + config text, visibility varchar(256) CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', - result jsonb, + result text, sequential_number integer NOT NULL, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, diff --git a/models.yml b/models.yml index 0227974b..863352dc 100644 --- a/models.yml +++ b/models.yml @@ -3428,7 +3428,7 @@ poll: - single_transferable_vote restriction_mode: A config: - type: JSON + type: text description: Values to configure the poll. Depends on the value in poll/method. restriction_mode: A visibility: @@ -3449,7 +3449,7 @@ poll: default: created restriction_mode: A result: - type: JSON + type: text description: Calculated result. The format depends on the value in poll/method restriction_mode: B sequential_number: @@ -4369,7 +4369,7 @@ history_position: to: history_entry/position_id restriction_mode: A on_delete: CASCADE - + history_entry: id: *id_field entries: From 6d5c2ac3a607fefcd78c88d57bfec9fb43f9dd1b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 8 Sep 2025 15:30:07 +0200 Subject: [PATCH 106/142] Add field live_voting_enabled --- dev/sql/schema_relational.sql | 6 ++++-- models.yml | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index c94b9514..442dfea1 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '06ffcb50123dd1f730ade66d92b05580' +-- MODELS_YML_CHECKSUM = '48abcaec341feb06556ec8e9d5552229' -- Database parameters @@ -920,6 +920,7 @@ CREATE TABLE poll_t ( visibility varchar(256) CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', result text, + live_voting_enabled boolean DEFAULT False, sequential_number integer NOT NULL, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -932,7 +933,8 @@ CREATE TABLE poll_t ( comment on column poll_t.config is 'Values to configure the poll. Depends on the value in poll/method.'; -comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method'; +comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually.'; +comment on column poll_t.live_voting_enabled is 'If true, the vote service sends the votes of the users to the autoupdate service.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; diff --git a/models.yml b/models.yml index 863352dc..71b4c51c 100644 --- a/models.yml +++ b/models.yml @@ -3450,8 +3450,13 @@ poll: restriction_mode: A result: type: text - description: Calculated result. The format depends on the value in poll/method + description: Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually. restriction_mode: B + live_voting_enabled: + type: boolean + default: false + description: If true, the vote service sends the votes of the users to the autoupdate service. + restriction_mode: A sequential_number: type: number description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. From b940876a18076661424da95e1569fc775d2c66bc Mon Sep 17 00:00:00 2001 From: Raimund Renkert Date: Thu, 11 Sep 2025 17:27:05 +0200 Subject: [PATCH 107/142] [rel-db] Update sequences after insert (#312) --- dev/sql/base_data.sql | 15 +++++++++++++++ dev/sql/test_data.sql | 16 +++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index b18e1e83..1792fa00 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -3,6 +3,8 @@ INSERT INTO theme_t (id, name) VALUES (1, 'standard theme'); +-- Increase sequence of theme_t.id to avoid errors. +SELECT nextval('theme_t_id_seq'); INSERT INTO organization_t (name, theme_id) @@ -13,6 +15,9 @@ INSERT INTO committee_t (id, name) VALUES (1, 'committee'); +-- Increase sequence of committee_t.id to avoid errors. +SELECT nextval('committee_t_id_seq'); + BEGIN; INSERT INTO @@ -28,6 +33,8 @@ meeting_t ( ) VALUES (1, 1, 2, 1, 1, 1, 1, 'meeting'); +-- Increase sequence of committee_t.id to avoid errors. +SELECT nextval('meeting_t_id_seq'); INSERT INTO group_t (id, name, meeting_id, permissions) @@ -46,6 +53,8 @@ VALUES }' ), (2, 'Admin', 1, DEFAULT); +-- Set sequence of group_t.id to avoid errors. +SELECT setval('group_t_id_seq', 2); INSERT INTO motion_workflow_t ( @@ -57,6 +66,8 @@ motion_workflow_t ( ) VALUES (1, 'Simple Workflow', 1, 1, 1); +-- Increase sequence of motion_workflow_t.id to avoid errors. +SELECT nextval('motion_workflow_t_id_seq'); INSERT INTO motion_state_t ( @@ -125,8 +136,12 @@ VALUES 'grey', 'do_not_merge' ); +-- Set sequence of motion_state_t.id to avoid errors. +SELECT setval('motion_state_t_id_seq', 4); + INSERT INTO projector_t (id, sequential_number, meeting_id) VALUES (1, 1, 1); +SELECT nextval('projector_t_id_seq'); COMMIT; diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index 98e2e21d..a3ac3bd4 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -8,11 +8,13 @@ BEGIN; INSERT INTO motion_state_t (id, name, weight, workflow_id, meeting_id) VALUES (5, 'motionState5', 1, 2, 2); +SELECT nextval('motion_state_t_id_seq'); INSERT INTO motion_workflow_t ( id, name, sequential_number, first_state_id, meeting_id ) VALUES (2, 'workflow2', 2, 4, 2); +SELECT nextval('motion_workflow_t_id_seq'); INSERT INTO meeting_t ( id, @@ -23,33 +25,40 @@ INSERT INTO meeting_t ( reference_projector_id, default_group_id ) - VALUES (2, 'name', 2, 2, 2, 2, 3); -INSERT INTO organization_tag_t (id, name, color) +SELECT nextval('meeting_t_id_seq'); + --generic-relation-list tagged_ids +INSERT INTO organization_tag_t (id, name, color) VALUES (1, 'tagA', '#cc3b03'); +SELECT nextval('organization_tag_t_id_seq'); --relation-list organization_tag_ids --relation 1:1 default_meeting_id -INSERT INTO committee (id, name, default_meeting_id) +INSERT INTO committee_t (id, name, default_meeting_id) VALUES (2, 'plenum', 2); +SELECT nextval('committee_t_id_seq'); INSERT INTO projector_t (id, sequential_number, meeting_id) VALUES (2, 2, 2); +SELECT nextval('projector_t_id_seq'); INSERT INTO group_t (id, name, meeting_id) VALUES (3, 'gruppe3', 2); +SELECT nextval('group_t_id_seq'); COMMIT; INSERT INTO organization_tag_t (id, name, color) VALUES (2, 'bunt', '#ffffff'); +SELECT nextval('organization_tag_t_id_seq'); INSERT INTO gm_organization_tag_tagged_ids_t (organization_tag_id, tagged_id) VALUES (2, 'meeting/1'); INSERT INTO topic_t (id, title, sequential_number, meeting_id) VALUES (1, 'Thema1', 1, 2); +SELECT nextval('topic_t_id_seq'); --agenda_item.content_object_id:topic.agenda_item_id gr:r INSERT INTO agenda_item_t (content_object_id, meeting_id) @@ -68,6 +77,7 @@ INSERT INTO poll_t ( meeting_id ) VALUES (1, 'Titel1', 'analog', 'fast', 'YNA', 'disabled', 1, 'topic/1', 2); +SELECT nextval('poll_t_id_seq'); --rl:rl committee_ids:user_ids INSERT INTO nm_committee_manager_ids_user_t (committee_id, user_id) From 5b34950ae1e286511361e26ceacb36b965b441db Mon Sep 17 00:00:00 2001 From: Raimund Renkert Date: Thu, 11 Sep 2025 17:28:03 +0200 Subject: [PATCH 108/142] Add references to history collections (#311) --- dev/sql/schema_relational.sql | 123 +++++++++++++++++----------------- models.yml | 13 +++- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 49a185e7..ddb5fb15 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '4963952911d7dec30826436bcf2d56f2' +-- MODELS_YML_CHECKSUM = '65aafbd8efddad3a70265441cbf116b1' -- Database parameters @@ -200,6 +200,7 @@ CREATE TABLE organization_t ( saml_metadata_idp text, saml_metadata_sp text, saml_private_key text, + vote_decrypt_public_main_key varchar(256), theme_id integer NOT NULL UNIQUE, users_email_sender varchar(256) DEFAULT 'OpenSlides', users_email_replyto varchar(256), @@ -221,13 +222,8 @@ This email was generated automatically.', comment on column organization_t.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; comment on column organization_t.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; +comment on column organization_t.vote_decrypt_public_main_key is 'Public key from vote decrypt to validate cryptographic votes.'; -/* - Fields without SQL definition for table organization - - organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema - -*/ CREATE TABLE user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -938,6 +934,7 @@ CREATE TABLE poll_t ( votescast decimal(16,6), entitled_users_at_stop jsonb, live_voting_enabled boolean DEFAULT False, + live_votes jsonb, sequential_number integer NOT NULL, crypt_key varchar(256), crypt_signature varchar(256), @@ -955,18 +952,13 @@ CREATE TABLE poll_t ( comment on column poll_t.live_voting_enabled is 'If true, the vote service sends the votes of the users to the autoupdate service.'; +comment on column poll_t.live_votes is 'dict from user to their vote. The value is null, when live voting is disabled.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; comment on column poll_t.crypt_key is 'base64 public key to cryptographic votes.'; comment on column poll_t.crypt_signature is 'base64 signature of cryptographic_key.'; comment on column poll_t.votes_raw is 'original form of decrypted votes.'; comment on column poll_t.votes_signature is 'base64 signature of votes_raw field.'; -/* - Fields without SQL definition for table poll - - poll/live_votes: type:JSON is marked as a calculated field and not generated in schema - -*/ CREATE TABLE option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1136,6 +1128,7 @@ CREATE TABLE projection_t ( stable boolean DEFAULT False, weight integer, type varchar(256), + content jsonb, current_projector_id integer, preview_projector_id integer, history_projector_id integer, @@ -1157,12 +1150,6 @@ CREATE TABLE projection_t ( -/* - Fields without SQL definition for table projection - - projection/content: type:JSON is marked as a calculated field and not generated in schema - -*/ CREATE TABLE projector_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1237,7 +1224,8 @@ CREATE TABLE import_preview_t ( CREATE TABLE history_position_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, timestamp timestamptz, - original_user_id integer + original_user_id integer, + user_id integer ); @@ -1246,7 +1234,14 @@ CREATE TABLE history_position_t ( CREATE TABLE history_entry_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, entries varchar(256)[], - original_model_id varchar(256) + original_model_id varchar(256), + model_id varchar(100), + model_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'user' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + model_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'motion' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + model_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'assignment' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_model_id_part1 CHECK (split_part(model_id, '/', 1) IN ('user','motion','assignment')), + position_id integer NOT NULL, + meeting_id integer ); @@ -1464,6 +1459,8 @@ CREATE VIEW "user" AS SELECT *, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, (select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, (select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, +(select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, ( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu @@ -1611,7 +1608,8 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_countdown_in_meeting_id = m.id) as default_projector_countdown_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_assignment_poll_in_meeting_id = m.id) as default_projector_assignment_poll_ids, (select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_motion_poll_in_meeting_id = m.id) as default_projector_motion_poll_ids, -(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids +(select array_agg(p.id ORDER BY p.id) from projector_t p where p.used_as_default_projector_for_poll_in_meeting_id = m.id) as default_projector_poll_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.meeting_id = m.id) as relevant_history_entry_ids FROM meeting_t m; comment on column "meeting_t".is_active_in_organization_id is 'Backrelation and boolean flag at once'; @@ -1708,7 +1706,8 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_motion_id = m.id) as tag_ids, (select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_motion_id = m.id) as attachment_meeting_mediafile_ids, (select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_id = m.id) as projection_ids, -(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids +(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.content_object_id_motion_id = m.id) as personal_note_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_motion_id = m.id) as history_entry_ids FROM motion_t m; @@ -1789,7 +1788,8 @@ CREATE VIEW "assignment" AS SELECT *, (select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, (select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, (select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_assignment_id = a.id) as history_entry_ids FROM assignment_t a; @@ -1876,7 +1876,9 @@ CREATE VIEW "action_worker" AS SELECT * FROM action_worker_t a; CREATE VIEW "import_preview" AS SELECT * FROM import_preview_t i; -CREATE VIEW "history_position" AS SELECT * FROM history_position_t h; +CREATE VIEW "history_position" AS SELECT *, +(select array_agg(he.id ORDER BY he.id) from history_entry_t he where he.position_id = h.id) as entry_ids +FROM history_position_t h; CREATE VIEW "history_entry" AS SELECT * FROM history_entry_t h; @@ -2089,6 +2091,14 @@ ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_u ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE history_position_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; + +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(position_id) REFERENCES history_position_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + -- Create triggers checking foreign_id not null for relation-lists @@ -2981,12 +2991,29 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_position'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_position_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_history_position_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON history_position_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); + CREATE TRIGGER tr_log_history_entry AFTER INSERT OR UPDATE OR DELETE ON history_entry_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_entry'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_entry_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_user_model_id_user_id AFTER INSERT OR UPDATE OF model_id_user_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user','model_id_user_id'); + +CREATE TRIGGER tr_log_motion_model_id_motion_id AFTER INSERT OR UPDATE OF model_id_motion_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','model_id_motion_id'); + +CREATE TRIGGER tr_log_assignment_model_id_assignment_id AFTER INSERT OR UPDATE OF model_id_assignment_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','model_id_assignment_id'); +CREATE TRIGGER tr_log_history_entry_t_position_id AFTER INSERT OR UPDATE OF position_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('history_position', 'position_id'); +CREATE TRIGGER tr_log_history_entry_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + + /* Relation-list infos Generated: What will be generated for left field FIELD: a usual Database field @@ -3029,10 +3056,8 @@ SQL nt:1r => user/vote_ids:-> vote/user_id SQL nt:1r => user/delegated_vote_ids:-> vote/delegated_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids -*** nt:1t => user/history_position_ids:-> history_position/user_id - Type combination not implemented: nt:1t on field user/history_position_ids -*** nt:1Gt => user/history_entry_ids:-> history_entry/model_id - Type combination not implemented: nt:1Gt on field user/history_entry_ids +SQL nt:1r => user/history_position_ids:-> history_position/user_id +SQL nt:1Gr => user/history_entry_ids:-> history_entry/model_id SQL nts:nts => user/meeting_ids:-> meeting/user_ids FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids @@ -3158,8 +3183,7 @@ SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_pr FIELD 1rR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id FIELD 1r:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id FIELD 1r:1t => meeting/anonymous_group_id:-> group/anonymous_group_for_meeting_id -*** nt:1t => meeting/relevant_history_entry_ids:-> history_entry/meeting_id - Type combination not implemented: nt:1t on field meeting/relevant_history_entry_ids +SQL nt:1r => meeting/relevant_history_entry_ids:-> history_entry/meeting_id SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id @@ -3256,8 +3280,7 @@ SQL nt:nGt => motion/attachment_meeting_mediafile_ids:-> meeting_mediafile/attac SQL nt:1GrR => motion/projection_ids:-> projection/content_object_id SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id FIELD 1rR:nt => motion/meeting_id:-> meeting/motion_ids -*** nt:1Gt => motion/history_entry_ids:-> history_entry/model_id - Type combination not implemented: nt:1Gt on field motion/history_entry_ids +SQL nt:1Gr => motion/history_entry_ids:-> history_entry/model_id FIELD 1rR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids @@ -3337,8 +3360,7 @@ SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids SQL nt:nGt => assignment/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id FIELD 1rR:nt => assignment/meeting_id:-> meeting/assignment_ids -*** nt:1Gt => assignment/history_entry_ids:-> history_entry/model_id - Type combination not implemented: nt:1Gt on field assignment/history_entry_ids +SQL nt:1Gr => assignment/history_entry_ids:-> history_entry/model_id FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids @@ -3425,34 +3447,13 @@ FIELD 1r:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids FIELD 1rR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids -*** 1t:nt => history_position/user_id:-> user/history_position_ids - Type combination not implemented: 1t:nt on field history_position/user_id -*** nt:1tR => history_position/entry_ids:-> history_entry/position_id - Type combination not implemented: nt:1tR on field history_position/entry_ids +FIELD 1r:nt => history_position/user_id:-> user/history_position_ids +SQL nt:1rR => history_position/entry_ids:-> history_entry/position_id -*** 1Gt:nt,nt,nt => history_entry/model_id:-> user/history_entry_ids,motion/history_entry_ids,assignment/history_entry_ids - Type combination not implemented: 1Gt:nt on field history_entry/model_id -*** 1tR:nt => history_entry/position_id:-> history_position/entry_ids - Type combination not implemented: 1tR:nt on field history_entry/position_id -*** 1t:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids - Type combination not implemented: 1t:nt on field history_entry/meeting_id +FIELD 1Gr:nt,nt,nt => history_entry/model_id:-> user/history_entry_ids,motion/history_entry_ids,assignment/history_entry_ids +FIELD 1rR:nt => history_entry/position_id:-> history_position/entry_ids +FIELD 1r:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids */ -/* -There are 13 errors/warnings - organization/vote_decrypt_public_main_key: type:string is marked as a calculated field and not generated in schema - user/history_position_ids: Type combination not implemented: nt:1t on field user/history_position_ids - user/history_entry_ids: Type combination not implemented: nt:1Gt on field user/history_entry_ids - meeting/relevant_history_entry_ids: Type combination not implemented: nt:1t on field meeting/relevant_history_entry_ids - motion/history_entry_ids: Type combination not implemented: nt:1Gt on field motion/history_entry_ids - poll/live_votes: type:JSON is marked as a calculated field and not generated in schema - assignment/history_entry_ids: Type combination not implemented: nt:1Gt on field assignment/history_entry_ids - projection/content: type:JSON is marked as a calculated field and not generated in schema - history_position/user_id: Type combination not implemented: 1t:nt on field history_position/user_id - history_position/entry_ids: Type combination not implemented: nt:1tR on field history_position/entry_ids - history_entry/model_id: Type combination not implemented: 1Gt:nt on field history_entry/model_id - history_entry/position_id: Type combination not implemented: 1tR:nt on field history_entry/position_id - history_entry/meeting_id: Type combination not implemented: 1t:nt on field history_entry/meeting_id -*/ /* Missing attribute handling for constant, on_delete, equal_fields, unique, deferred */ \ No newline at end of file diff --git a/models.yml b/models.yml index 5cb4a327..6525b2db 100644 --- a/models.yml +++ b/models.yml @@ -218,7 +218,7 @@ organization: vote_decrypt_public_main_key: type: string description: Public key from vote decrypt to validate cryptographic votes. - calculated: true + # calculated: true restriction_mode: A committee_ids: @@ -3528,7 +3528,7 @@ poll: restriction_mode: A live_votes: type: JSON - calculated: true + # calculated: true description: dict from user to their vote. The value is null, when live voting is disabled. restriction_mode: C sequential_number: @@ -4280,7 +4280,7 @@ projection: content: type: JSON - calculated: true + # calculated: true restriction_mode: A current_projector_id: @@ -4542,6 +4542,7 @@ history_position: type: relation to: user/history_position_ids restriction_mode: A + reference: user entry_ids: type: relation-list to: history_entry/position_id @@ -4566,13 +4567,19 @@ history_entry: - assignment field: history_entry_ids restriction_mode: A + reference: + - user + - motion + - assignment position_id: type: relation to: history_position/entry_ids required: true restriction_mode: A constant: true + reference: history_position meeting_id: type: relation to: meeting/relevant_history_entry_ids restriction_mode: A + reference: meeting From f96efec3d86ad28ecca8103de8c573b6efd92e6c Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 13 Sep 2025 18:12:23 +0200 Subject: [PATCH 109/142] restriciton modes and new publish system --- models.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/models.yml b/models.yml index 71b4c51c..43fdbacb 100644 --- a/models.yml +++ b/models.yml @@ -3445,17 +3445,16 @@ poll: - created - started - finished - - published default: created restriction_mode: A result: type: text description: Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually. restriction_mode: B - live_voting_enabled: + published: type: boolean default: false - description: If true, the vote service sends the votes of the users to the autoupdate service. + description: If true, users can see the result. restriction_mode: A sequential_number: type: number @@ -3513,12 +3512,12 @@ vote: id: *id_field weight: type: decimal(6) - restriction_mode: A + restriction_mode: B constant: true default: "1.000000" value: type: text - restriction_mode: A + restriction_mode: B constant: true poll_id: type: relation @@ -3532,12 +3531,12 @@ vote: type: relation reference: user to: user/acting_vote_ids - restriction_mode: A + restriction_mode: B represented_user_id: type: relation reference: user to: user/represented_vote_ids - restriction_mode: A + restriction_mode: B meeting_id: type: relation reference: meeting From 5fc2e685258f34061fe6bde2d71a3e4f4d7b501b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 13 Sep 2025 20:05:12 +0200 Subject: [PATCH 110/142] make allow_invalid a separat attribute --- dev/sql/schema_relational.sql | 9 ++++++--- models.yml | 17 +++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index ff3ed057..07a1d303 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '4b0a18686855779e93f1edb269b20eaa' +-- MODELS_YML_CHECKSUM = '45a8410dde8705474ea7e4c08430f1df' -- Database parameters @@ -524,8 +524,8 @@ This email was generated automatically.', poll_default_type varchar(256) DEFAULT 'analog', poll_default_method varchar(256), poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', - poll_default_backend varchar(256) CONSTRAINT enum_meeting_poll_default_backend CHECK (poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', poll_default_live_voting_enabled boolean DEFAULT False, + poll_default_allow_invalid boolean DEFAULT False, poll_couple_countdown boolean DEFAULT True, logo_projector_main_id integer UNIQUE, logo_projector_header_id integer UNIQUE, @@ -559,7 +559,8 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.live_voting_enabled'' option suggested to user. Is not used in the validations.'; +comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.published'' before finished option suggested to user. Is not used in the validations.'; +comment on column meeting_t.poll_default_allow_invalid is 'Defines defaut `poll.allow_invalid` option suggested to user.'; CREATE TABLE structure_level_t ( @@ -920,6 +921,7 @@ CREATE TABLE poll_t ( state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished')) DEFAULT 'created', result text, published boolean DEFAULT False, + allow_invalid boolean DEFAULT False, sequential_number integer NOT NULL, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -934,6 +936,7 @@ CREATE TABLE poll_t ( comment on column poll_t.config is 'Values to configure the poll. Depends on the value in poll/method.'; comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually.'; comment on column poll_t.published is 'If true, users can see the result.'; +comment on column poll_t.allow_invalid is 'If true, the vote service does not validate. This is always the case for secret polls.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; diff --git a/models.yml b/models.yml index 74996dc8..9a531879 100644 --- a/models.yml +++ b/models.yml @@ -1630,15 +1630,15 @@ meeting: type: relation-list to: group/used_as_poll_default_id restriction_mode: B - poll_default_backend: - type: string - enum: *poll_backends - default: fast - restriction_mode: B poll_default_live_voting_enabled: type: boolean default: false - description: Defines default 'poll.live_voting_enabled' option suggested to user. Is not used in the validations. + description: Defines default 'poll.published' before finished option suggested to user. Is not used in the validations. + restriction_mode: B + poll_default_allow_invalid: + type: boolean + default: false + description: Defines defaut `poll.allow_invalid` option suggested to user. restriction_mode: B poll_couple_countdown: type: boolean @@ -3454,6 +3454,11 @@ poll: default: false description: If true, users can see the result. restriction_mode: A + allow_invalid: + type: boolean + default: false + description: If true, the vote service does not validate. This is always the case for secret polls. + restriction_mode: A sequential_number: type: number description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. From f47728e183455a0aa6b56845b3097eda354f0870 Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 24 Sep 2025 10:06:10 +0200 Subject: [PATCH 111/142] [rel-db] Introduce more relations/constraints / change handling of MODELS (#306) * allow more relations with required fields * change handling of models variable * add not null / required constraint for 1:1 relations * add list_of_speakers to test_data --- dev/Makefile | 4 +- dev/sql/schema_relational.sql | 85 ++++++++++++++++++++++++++ dev/sql/test_data.sql | 8 +++ dev/src/generate_sql_schema.py | 107 ++++++++++++++++++++++++++++----- dev/src/helper_get_names.py | 54 +++++++++++++++-- 5 files changed, 238 insertions(+), 20 deletions(-) diff --git a/dev/Makefile b/dev/Makefile index c8f120f4..e8ea715d 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -34,10 +34,10 @@ sqlfluff: sqlfluff fix --dialect postgres --verbose validate-models: - python src/validate.py + python -m src.validate generate-relational-schema: - python src/generate_sql_schema.py + python -m src.generate_sql_schema drop-database: dropdb -f -e --if-exists -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index ddb5fb15..567e5b3b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -173,6 +173,41 @@ CREATE TABLE os_notify_log_t ( CONSTRAINT unique_fqid_xact_id_operation UNIQUE (operation,fqid,xact_id) ); +CREATE FUNCTION check_not_null_for_1_1() RETURNS trigger as $not_null_trigger$ +-- usage with 3 parameters IN TRIGGER DEFINITION: +-- table_name: relation to check, usually a view +-- column_name: field to check, usually a field in a view +-- foreign_key: field name of triggered table, that will be used to SELECT +-- the values to check the not null. Can be empty on INSERT as then unused. +DECLARE + table_name TEXT := TG_ARGV[0]; + column_name TEXT := TG_ARGV[1]; + foreign_key TEXT := TG_ARGV[2]; + foreign_id INTEGER; + counted INTEGER; +BEGIN + IF (TG_OP = 'INSERT') THEN + -- in case of INSERT the view is checked on itself so the own id is applicable + foreign_id := NEW.id; + ELSIF (TG_OP = 'UPDATE') OR (TG_OP = 'DELETE') THEN + foreign_id := hstore(OLD) -> foreign_key; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; + IF (counted IS NULL) THEN + -- if the earlier referenced row was deleted (in the same transaction) we can quit. + RETURN NULL; + END IF; + END IF; + + IF (foreign_id IS NOT NULL) THEN + EXECUTE format('SELECT %I FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; + IF (counted is NULL) THEN + RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %/%/% from relationship before %/%', TG_NAME, table_name, foreign_id, column_name, OLD.id, foreign_key; + END IF; + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return +END; +$not_null_trigger$ language plpgsql; + -- Type definitions @@ -2101,6 +2136,56 @@ ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) +-- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships + +-- definition trigger not null for topic.agenda_item_id against agenda_item.content_object_id_topic_id +CREATE CONSTRAINT TRIGGER tr_i_topic_agenda_item_id AFTER INSERT ON topic_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_topic_agenda_item_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', 'content_object_id_topic_id'); + +-- definition trigger not null for topic.list_of_speakers_id against list_of_speakers.content_object_id_topic_id +CREATE CONSTRAINT TRIGGER tr_i_topic_list_of_speakers_id AFTER INSERT ON topic_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_topic_list_of_speakers_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', 'content_object_id_topic_id'); + + +-- definition trigger not null for motion.list_of_speakers_id against list_of_speakers.content_object_id_motion_id +CREATE CONSTRAINT TRIGGER tr_i_motion_list_of_speakers_id AFTER INSERT ON motion_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_motion_list_of_speakers_id AFTER UPDATE OF content_object_id_motion_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id', 'content_object_id_motion_id'); + + +-- definition trigger not null for motion_block.list_of_speakers_id against list_of_speakers.content_object_id_motion_block_id +CREATE CONSTRAINT TRIGGER tr_i_motion_block_list_of_speakers_id AFTER INSERT ON motion_block_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_motion_block_list_of_speakers_id AFTER UPDATE OF content_object_id_motion_block_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', 'content_object_id_motion_block_id'); + + +-- definition trigger not null for assignment.list_of_speakers_id against list_of_speakers.content_object_id_assignment_id +CREATE CONSTRAINT TRIGGER tr_i_assignment_list_of_speakers_id AFTER INSERT ON assignment_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_assignment_list_of_speakers_id AFTER UPDATE OF content_object_id_assignment_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'content_object_id_assignment_id'); + + +-- definition trigger not null for poll_candidate_list.option_id against option.content_object_id_poll_candidate_list_id +CREATE CONSTRAINT TRIGGER tr_i_poll_candidate_list_option_id AFTER INSERT ON poll_candidate_list_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_poll_candidate_list_option_id AFTER UPDATE OF content_object_id_poll_candidate_list_id OR DELETE ON option_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', 'content_object_id_poll_candidate_list_id'); + + + -- Create triggers checking foreign_id not null for relation-lists -- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index a3ac3bd4..0e6c4140 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -56,13 +56,21 @@ SELECT nextval('organization_tag_t_id_seq'); INSERT INTO gm_organization_tag_tagged_ids_t (organization_tag_id, tagged_id) VALUES (2, 'meeting/1'); +BEGIN; INSERT INTO topic_t (id, title, sequential_number, meeting_id) VALUES (1, 'Thema1', 1, 2); SELECT nextval('topic_t_id_seq'); +--list_of_speakers.content_object_id:topic.list_of_speakers_id gr:r +INSERT INTO list_of_speakers_t ( + id, content_object_id, sequential_number, meeting_id +) +VALUES (1, 'topic/1', 1, 2); + --agenda_item.content_object_id:topic.agenda_item_id gr:r INSERT INTO agenda_item_t (content_object_id, meeting_id) VALUES ('topic/1', 2); +COMMIT; --rl:gr topic.poll_ids:poll.content_object_id INSERT INTO poll_t ( diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 92b90808..82000806 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -11,9 +11,9 @@ from sqlfluff import fix -from helper_get_names import FieldSqlErrorType # type: ignore -from helper_get_names import ( +from .helper_get_names import ( KEYSEPARATOR, + FieldSqlErrorType, HelperGetNames, InternalHelper, TableFieldType, @@ -32,6 +32,7 @@ class SchemaZoneTexts(TypedDict, total=False): post_view: str alter_table: str alter_table_final: str + create_trigger_1_1_relation_not_null: str create_trigger_relationlistnotnull: str create_trigger_unique_ids_pair_code: str create_trigger_notify: str @@ -66,6 +67,7 @@ class SubstDict(TypedDict, total=False): class GenerateCodeBlocks: """Main work is done here by recursing the models and their fields and determine the method to use""" + models = MODELS intermediate_tables: dict[str, str] = ( {} ) # Key=Name, data: collected content of table @@ -73,7 +75,7 @@ class GenerateCodeBlocks: @classmethod def generate_the_code( cls, - ) -> tuple[str, str, str, str, str, list[str], str, str, str, str, list[str]]: + ) -> tuple[str, str, str, str, str, list[str], str, str, str, str, str, list[str]]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -85,6 +87,7 @@ def generate_the_code( im_table_code: Code for intermediate tables. n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field + create_trigger_1_1_relation_not_null_code: Definitions of triggers calling check_not_null_for_1_1_relation create_trigger_relationlistnotnull_code: Definitions of triggers calling check_not_null_for_relation_lists create_trigger_unique_ids_pair_code: Definitions of triggers calling check_unique_ids_pair create_trigger_notify_code: Definitions of triggers calling notify_modified_models @@ -114,6 +117,7 @@ def generate_the_code( table_name_code: str = "" view_name_code: str = "" alter_table_final_code: str = "" + create_trigger_1_1_relation_not_null_code: str = "" create_trigger_relationlistnotnull_code: str = "" create_trigger_unique_ids_pair_code: str = "" create_trigger_notify_code: str = "" @@ -121,8 +125,10 @@ def generate_the_code( missing_handled_attributes = [] im_table_code = "" errors: list[str] = [] + if not cls.models: + cls.models = MODELS - for table_name, fields in MODELS.items(): + for table_name, fields in cls.models.items(): if table_name in ["_migration_index", "_meta"]: continue @@ -163,6 +169,8 @@ def generate_the_code( view_name_code += code if code := schema_zone_texts["alter_table_final"]: alter_table_final_code += code + "\n" + if code := schema_zone_texts["create_trigger_1_1_relation_not_null"]: + create_trigger_1_1_relation_not_null_code += code + "\n" if code := schema_zone_texts["create_trigger_relationlistnotnull"]: create_trigger_relationlistnotnull_code += code + "\n" if code := schema_zone_texts["create_trigger_unique_ids_pair_code"]: @@ -191,6 +199,7 @@ def generate_the_code( final_info_code, missing_handled_attributes, im_table_code, + create_trigger_1_1_relation_not_null_code, create_trigger_relationlistnotnull_code, create_trigger_unique_ids_pair_code, create_trigger_notify_code, @@ -345,22 +354,27 @@ def get_relation_type( elif state == FieldSqlErrorType.SQL: if sql := fix(fdata.get("sql", "")): text["view"] = sql + ",\n" - elif foreign_table_field.field_def["type"] == "generic-relation": - text["view"] = cls.get_sql_for_relation_1_1( - table_name, - fname, - foreign_table_field.ref_column, - foreign_table, - f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}", - ) else: + if foreign_table_field.field_def["type"] == "generic-relation": + foreign_column = f"{foreign_table_field.column}_{own_table_field.table}_{own_table_field.ref_column}" + else: + foreign_column = foreign_table_field.column text["view"] = cls.get_sql_for_relation_1_1( table_name, fname, foreign_table_field.ref_column, foreign_table, - cast(str, foreign_table_field.column), + foreign_column, ) + if own_table_field.field_def.get("required"): + text["create_trigger_1_1_relation_not_null"] = ( + cls.get_trigger_check_not_null_for_1_1_relation( + own_table_field.table, + own_table_field.column, + foreign_table_field.table, + foreign_column, + ) + ) if comment := fdata.get("description"): text["post_view"] += Helper.get_post_view_comment( HelperGetNames.get_view_name(table_name), fname, comment @@ -536,7 +550,9 @@ def get_sql_for_relation_n_1( if foreign_table_column: query += COND_TEMPLATE.format(foreign_table_column) else: - assert foreign_table_ref_column == (col := foreign_table_column) + assert foreign_table_ref_column == ( + col := foreign_table_column + ), f"own {col} and foreign {foreign_table_ref_column} should be equal" arr1 = AGG_TEMPLATE.format(f"{col}_1", f"{col}_1") + COND_TEMPLATE.format( f"{col}_2" ) @@ -546,6 +562,23 @@ def get_sql_for_relation_n_1( query = f"select array_cat(({arr1}), ({arr2}))" return f"({query}) as {fname},\n" + @classmethod + def get_trigger_check_not_null_for_1_1_relation( + cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str + ) -> str: + own_table_t = HelperGetNames.get_table_name(own_table) + foreign_table_t = HelperGetNames.get_table_name(foreign_table) + return dedent( + f""" + -- definition trigger not null for {own_table}.{own_column} against {foreign_table}.{foreign_column} + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_table}', '{own_column}', ''); + + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_table}', '{own_column}', '{foreign_column}'); + """ + ) + @classmethod def get_trigger_check_not_null_for_relation_lists( cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str @@ -866,6 +899,47 @@ class Helper: ); """ ) + + for type_, field_check in {"1_1": "%I"}.items(): + FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( + f""" + CREATE FUNCTION check_not_null_for_{type_}() RETURNS trigger as $not_null_trigger$ + -- usage with 3 parameters IN TRIGGER DEFINITION: + -- table_name: relation to check, usually a view + -- column_name: field to check, usually a field in a view + -- foreign_key: field name of triggered table, that will be used to SELECT + -- the values to check the not null. Can be empty on INSERT as then unused. + DECLARE + table_name TEXT := TG_ARGV[0]; + column_name TEXT := TG_ARGV[1]; + foreign_key TEXT := TG_ARGV[2]; + foreign_id INTEGER; + counted INTEGER; + BEGIN + IF (TG_OP = 'INSERT') THEN + -- in case of INSERT the view is checked on itself so the own id is applicable + foreign_id := NEW.id; + ELSIF (TG_OP = 'UPDATE') OR (TG_OP = 'DELETE') THEN + foreign_id := hstore(OLD) -> foreign_key; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; + IF (counted IS NULL) THEN + -- if the earlier referenced row was deleted (in the same transaction) we can quit. + RETURN NULL; + END IF; + END IF; + + IF (foreign_id IS NOT NULL) THEN + EXECUTE format('SELECT {field_check} FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; + IF (counted is NULL) THEN + RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %/%/% from relationship before %/%', TG_NAME, table_name, foreign_id, column_name, OLD.id, foreign_key; + END IF; + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return + END; + $not_null_trigger$ language plpgsql; + """ + ) + FIELD_TEMPLATE = string.Template( " ${field_name} ${type}${primary_key}${required}${unique}${check_enum}${minimum}${minLength}${default},\n" ) @@ -1431,6 +1505,7 @@ def main() -> None: final_info_code, missing_handled_attributes, im_table_code, + create_trigger_1_1_relation_not_null_code, create_trigger_relationlistnotnull_code, create_trigger_unique_ids_pair_code, create_trigger_notify_code, @@ -1453,6 +1528,10 @@ def main() -> None: dest.write(view_name_code) dest.write("\n\n-- Alter table relations\n") dest.write(alter_table_code) + dest.write( + "\n\n-- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships\n" + ) + dest.write(create_trigger_1_1_relation_not_null_code) dest.write( "\n\n-- Create triggers checking foreign_id not null for relation-lists\n" ) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 1b1a7f73..6ce46b6b 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -185,11 +185,11 @@ def get_minlength_constraint_name( @staticmethod @max_length - def get_not_null_rel_list_insert_trigger_name( + def get_not_null_insert_trigger_name_base( table_name: str, column_name: str, ) -> str: - """gets the name of the insert trigger for not null on relation lists""" + """gets the name of the insert trigger for not null""" name = f"tr_i_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") @@ -198,17 +198,61 @@ def get_not_null_rel_list_insert_trigger_name( @staticmethod @max_length - def get_not_null_rel_list_upd_del_trigger_name( + def get_not_null_upd_del_trigger_name_base( table_name: str, column_name: str, ) -> str: - """gets the name of the update/delete trigger for not null on relation lists""" + """gets the name of the update/delete trigger for not null""" name = f"tr_ud_{table_name}_{column_name}"[: HelperGetNames.MAX_LEN] if name in HelperGetNames.trigger_unique_list: raise Exception(f"trigger {name} is not unique!") HelperGetNames.trigger_unique_list.append(name) return name + @staticmethod + @max_length + def get_not_null_rel_list_insert_trigger_name( + table_name: str, + column_name: str, + ) -> str: + """gets the name of the insert trigger for not null on relation lists""" + return HelperGetNames.get_not_null_insert_trigger_name_base( + table_name, column_name + ) + + @staticmethod + @max_length + def get_not_null_rel_list_upd_del_trigger_name( + table_name: str, + column_name: str, + ) -> str: + """gets the name of the update/delete trigger for not null on relation lists""" + return HelperGetNames.get_not_null_upd_del_trigger_name_base( + table_name, column_name + ) + + @staticmethod + @max_length + def get_not_null_1_1_rel_insert_trigger_name( + table_name: str, + column_name: str, + ) -> str: + """gets the name of the insert trigger for not null on 1:1 relations""" + return HelperGetNames.get_not_null_insert_trigger_name_base( + table_name, column_name + ) + + @staticmethod + @max_length + def get_not_null_1_1_rel_upd_del_trigger_name( + table_name: str, + column_name: str, + ) -> str: + """gets the name of the update/delete trigger for not null on 1:1 relations""" + return HelperGetNames.get_not_null_upd_del_trigger_name_base( + table_name, column_name + ) + @staticmethod @max_length def get_notify_trigger_name( @@ -394,6 +438,8 @@ def generate_field_or_sql_decision( ("1t", "1rR"): (FieldSqlErrorType.SQL, False), ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), + ("1tR", "1r"): (FieldSqlErrorType.SQL, False), + ("1tR", "1rR"): (FieldSqlErrorType.SQL, False), ("nGt", "nt"): (FieldSqlErrorType.SQL, True), ("nr", ""): (FieldSqlErrorType.SQL, True), ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), From b2e83a19cd9d6fd69e7e080bce986b2c25a90fdd Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Fri, 26 Sep 2025 11:14:43 +0200 Subject: [PATCH 112/142] Improve check_not_null_for_relation_lists trigger (#308) --- dev/sql/base_data.sql | 25 ++++- dev/sql/schema_relational.sql | 175 +++++++++++++++++---------------- dev/sql/test_data.sql | 22 ++++- dev/src/generate_sql_schema.py | 61 +++--------- 4 files changed, 147 insertions(+), 136 deletions(-) diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index 1792fa00..8d2901a4 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -140,8 +140,27 @@ VALUES SELECT setval('motion_state_t_id_seq', 4); -INSERT INTO projector_t (id, sequential_number, meeting_id) -VALUES (1, 1, 1); -SELECT nextval('projector_t_id_seq'); +INSERT INTO +projector_t +( + id, + sequential_number, + meeting_id, + used_as_default_projector_for_agenda_item_list_in_meeting_id, + used_as_default_projector_for_topic_in_meeting_id, + used_as_default_projector_for_list_of_speakers_in_meeting_id, + used_as_default_projector_for_current_los_in_meeting_id, + used_as_default_projector_for_motion_in_meeting_id, + used_as_default_projector_for_amendment_in_meeting_id, + used_as_default_projector_for_motion_block_in_meeting_id, + used_as_default_projector_for_assignment_in_meeting_id, + used_as_default_projector_for_mediafile_in_meeting_id, + used_as_default_projector_for_message_in_meeting_id, + used_as_default_projector_for_countdown_in_meeting_id, + used_as_default_projector_for_assignment_poll_in_meeting_id, + used_as_default_projector_for_motion_poll_in_meeting_id, + used_as_default_projector_for_poll_in_meeting_id +) +VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); COMMIT; diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 567e5b3b..bcc63741 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '65aafbd8efddad3a70265441cbf116b1' +-- MODELS_YML_CHECKSUM = 'cc31a7aa72f26043a892bcef4ce3c0ad' -- Database parameters @@ -19,46 +19,6 @@ SET log_min_messages TO WARNING; CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine -CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ --- usage with 3 parameters IN TRIGGER DEFINITION: --- table_name of field to check, usually a field in a view --- column_name of field to check --- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null. -DECLARE - table_name TEXT; - column_name TEXT; - foreign_key TEXT; - foreign_id INTEGER; - counted INTEGER; -begin - table_name = TG_ARGV[0]; - column_name = TG_ARGV[1]; - foreign_key = TG_ARGV[2]; - - IF (TG_OP = 'INSERT') THEN - foreign_id := hstore(NEW) -> foreign_key; - IF (foreign_id is NOT NULL) THEN - foreign_id = NULL; -- no need to ask DB - END IF; - ELSIF (TG_OP = 'UPDATE') THEN - foreign_id := hstore(NEW) -> foreign_key; - IF (foreign_id is NULL) THEN - foreign_id = OLD.used_as_default_projector_for_topic_in_meeting_id; - END IF; - ELSIF (TG_OP = 'DELETE') THEN - foreign_id := hstore(OLD) -> foreign_key; - END IF; - - IF (foreign_id IS NOT NULL) THEN - EXECUTE format('SELECT array_length(%I, 1) FROM %I where id = %s', column_name, table_name, foreign_id) INTO counted; - IF (counted is NULL) THEN - RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %.%', TG_NAME, table_name, column_name; - END IF; - END IF; - RETURN NULL; -- AFTER TRIGGER needs no return -end; -$not_null_trigger$ language plpgsql; - CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; @@ -185,11 +145,12 @@ DECLARE foreign_key TEXT := TG_ARGV[2]; foreign_id INTEGER; counted INTEGER; + error_message TEXT; BEGIN IF (TG_OP = 'INSERT') THEN -- in case of INSERT the view is checked on itself so the own id is applicable foreign_id := NEW.id; - ELSIF (TG_OP = 'UPDATE') OR (TG_OP = 'DELETE') THEN + ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN foreign_id := hstore(OLD) -> foreign_key; EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; IF (counted IS NULL) THEN @@ -201,7 +162,51 @@ BEGIN IF (foreign_id IS NOT NULL) THEN EXECUTE format('SELECT %I FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; IF (counted is NULL) THEN - RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %/%/% from relationship before %/%', TG_NAME, table_name, foreign_id, column_name, OLD.id, foreign_key; + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); + IF TG_OP IN ('UPDATE', 'DELETE') THEN + error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); + END IF; + RAISE EXCEPTION '%', error_message; + END IF; + END IF; + RETURN NULL; -- AFTER TRIGGER needs no return +END; +$not_null_trigger$ language plpgsql; + +CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ +-- usage with 3 parameters IN TRIGGER DEFINITION: +-- table_name: relation to check, usually a view +-- column_name: field to check, usually a field in a view +-- foreign_key: field name of triggered table, that will be used to SELECT +-- the values to check the not null. Can be empty on INSERT as then unused. +DECLARE + table_name TEXT := TG_ARGV[0]; + column_name TEXT := TG_ARGV[1]; + foreign_key TEXT := TG_ARGV[2]; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT; +BEGIN + IF (TG_OP = 'INSERT') THEN + -- in case of INSERT the view is checked on itself so the own id is applicable + foreign_id := NEW.id; + ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN + foreign_id := hstore(OLD) -> foreign_key; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; + IF (counted IS NULL) THEN + -- if the earlier referenced row was deleted (in the same transaction) we can quit. + RETURN NULL; + END IF; + END IF; + + IF (foreign_id IS NOT NULL) THEN + EXECUTE format('SELECT array_length(%I, 1) FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; + IF (counted is NULL) THEN + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); + IF TG_OP IN ('UPDATE', 'DELETE') THEN + error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); + END IF; + RAISE EXCEPTION '%', error_message; END IF; END IF; RETURN NULL; -- AFTER TRIGGER needs no return @@ -2189,114 +2194,114 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'opt -- Create triggers checking foreign_id not null for relation-lists -- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); -- definition trigger not null for meeting.default_projector_topic_ids against projector_t.used_as_default_projector_for_topic_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); -- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projector_t.used_as_default_projector_for_list_of_speakers_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); -- definition trigger not null for meeting.default_projector_current_los_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_los_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_los_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_los_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_los_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_ids against projector_t.used_as_default_projector_for_motion_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); -- definition trigger not null for meeting.default_projector_amendment_ids against projector_t.used_as_default_projector_for_amendment_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_block_ids against projector_t.used_as_default_projector_for_motion_block_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); -- definition trigger not null for meeting.default_projector_assignment_ids against projector_t.used_as_default_projector_for_assignment_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); -- definition trigger not null for meeting.default_projector_mediafile_ids against projector_t.used_as_default_projector_for_mediafile_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); -- definition trigger not null for meeting.default_projector_message_ids against projector_t.used_as_default_projector_for_message_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); -- definition trigger not null for meeting.default_projector_countdown_ids against projector_t.used_as_default_projector_for_countdown_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); -- definition trigger not null for meeting.default_projector_assignment_poll_ids against projector_t.used_as_default_projector_for_assignment_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_poll_ids against projector_t.used_as_default_projector_for_motion_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); -- definition trigger not null for meeting.default_projector_poll_ids against projector_t.used_as_default_projector_for_poll_in_meeting_id -CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); +CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', ''); -CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t +CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index 0e6c4140..717af358 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -39,8 +39,26 @@ INSERT INTO committee_t (id, name, default_meeting_id) VALUES (2, 'plenum', 2); SELECT nextval('committee_t_id_seq'); -INSERT INTO projector_t (id, sequential_number, meeting_id) -VALUES (2, 2, 2); +INSERT INTO projector_t ( + id, + sequential_number, + meeting_id, + used_as_default_projector_for_agenda_item_list_in_meeting_id, + used_as_default_projector_for_topic_in_meeting_id, + used_as_default_projector_for_list_of_speakers_in_meeting_id, + used_as_default_projector_for_current_los_in_meeting_id, + used_as_default_projector_for_motion_in_meeting_id, + used_as_default_projector_for_amendment_in_meeting_id, + used_as_default_projector_for_motion_block_in_meeting_id, + used_as_default_projector_for_assignment_in_meeting_id, + used_as_default_projector_for_mediafile_in_meeting_id, + used_as_default_projector_for_message_in_meeting_id, + used_as_default_projector_for_countdown_in_meeting_id, + used_as_default_projector_for_assignment_poll_in_meeting_id, + used_as_default_projector_for_motion_poll_in_meeting_id, + used_as_default_projector_for_poll_in_meeting_id +) +VALUES (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); SELECT nextval('projector_t_id_seq'); INSERT INTO group_t (id, name, meeting_id) diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 82000806..dda5d0e4 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -583,14 +583,15 @@ def get_trigger_check_not_null_for_1_1_relation( def get_trigger_check_not_null_for_relation_lists( cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str ) -> str: + own_table_t = HelperGetNames.get_table_name(own_table) foreign_table_t = HelperGetNames.get_table_name(foreign_table) return dedent( f""" -- definition trigger not null for {own_table}.{own_column} against {foreign_table_t}.{foreign_column} - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {foreign_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', '{foreign_column}'); + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', ''); - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', '{foreign_column}'); """ @@ -744,46 +745,6 @@ class Helper: """ CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine - CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ - -- usage with 3 parameters IN TRIGGER DEFINITION: - -- table_name of field to check, usually a field in a view - -- column_name of field to check - -- foreign_key field name of triggered table, that will be used to SELECT the values to check the not null. - DECLARE - table_name TEXT; - column_name TEXT; - foreign_key TEXT; - foreign_id INTEGER; - counted INTEGER; - begin - table_name = TG_ARGV[0]; - column_name = TG_ARGV[1]; - foreign_key = TG_ARGV[2]; - - IF (TG_OP = 'INSERT') THEN - foreign_id := hstore(NEW) -> foreign_key; - IF (foreign_id is NOT NULL) THEN - foreign_id = NULL; -- no need to ask DB - END IF; - ELSIF (TG_OP = 'UPDATE') THEN - foreign_id := hstore(NEW) -> foreign_key; - IF (foreign_id is NULL) THEN - foreign_id = OLD.used_as_default_projector_for_topic_in_meeting_id; - END IF; - ELSIF (TG_OP = 'DELETE') THEN - foreign_id := hstore(OLD) -> foreign_key; - END IF; - - IF (foreign_id IS NOT NULL) THEN - EXECUTE format('SELECT array_length(%I, 1) FROM %I where id = %s', column_name, table_name, foreign_id) INTO counted; - IF (counted is NULL) THEN - RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %.%', TG_NAME, table_name, column_name; - END IF; - END IF; - RETURN NULL; -- AFTER TRIGGER needs no return - end; - $not_null_trigger$ language plpgsql; - CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; @@ -900,7 +861,10 @@ class Helper: """ ) - for type_, field_check in {"1_1": "%I"}.items(): + for type_, field_check in { + "1_1": "%I", + "relation_lists": "array_length(%I, 1)", + }.items(): FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( f""" CREATE FUNCTION check_not_null_for_{type_}() RETURNS trigger as $not_null_trigger$ @@ -915,11 +879,12 @@ class Helper: foreign_key TEXT := TG_ARGV[2]; foreign_id INTEGER; counted INTEGER; + error_message TEXT; BEGIN IF (TG_OP = 'INSERT') THEN -- in case of INSERT the view is checked on itself so the own id is applicable foreign_id := NEW.id; - ELSIF (TG_OP = 'UPDATE') OR (TG_OP = 'DELETE') THEN + ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN foreign_id := hstore(OLD) -> foreign_key; EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; IF (counted IS NULL) THEN @@ -931,7 +896,11 @@ class Helper: IF (foreign_id IS NOT NULL) THEN EXECUTE format('SELECT {field_check} FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; IF (counted is NULL) THEN - RAISE EXCEPTION 'Trigger % Exception: NOT NULL CONSTRAINT VIOLATED for %/%/% from relationship before %/%', TG_NAME, table_name, foreign_id, column_name, OLD.id, foreign_key; + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); + IF TG_OP IN ('UPDATE', 'DELETE') THEN + error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); + END IF; + RAISE EXCEPTION '%', error_message; END IF; END IF; RETURN NULL; -- AFTER TRIGGER needs no return From 26540c6daf251a8274f5ef41b17213a27e3a8b6d Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 1 Oct 2025 18:37:15 +0200 Subject: [PATCH 113/142] Move gender to base_data --- dev/sql/base_data.sql | 2 ++ dev/sql/test_data.sql | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index 8d2901a4..00557c22 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -18,6 +18,8 @@ VALUES -- Increase sequence of committee_t.id to avoid errors. SELECT nextval('committee_t_id_seq'); +INSERT INTO gender_t (name) VALUES ('female'); + BEGIN; INSERT INTO diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index 717af358..f55727a2 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -1,7 +1,6 @@ -- This script should only be used for an database filled with base_data.sql. --relation --relation-list gender_ids -INSERT INTO gender_t (name) VALUES ('female'); INSERT INTO user_t (username, gender_id) VALUES ('tom', 1); BEGIN; From 9357b13d96255be3948a11afe5f3a88c3999a32c Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 3 Oct 2025 15:46:28 +0200 Subject: [PATCH 114/142] Remove relation from vote to meeting. --- dev/sql/schema_relational.sql | 11 ++--------- models.yml | 12 ------------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index afd6b26c..d9ca0106 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '45a8410dde8705474ea7e4c08430f1df' +-- MODELS_YML_CHECKSUM = '96a677040d25084cdd90c3408371672e' -- Database parameters @@ -986,8 +986,7 @@ CREATE TABLE vote_t ( value text, poll_id integer NOT NULL, acting_user_id integer, - represented_user_id integer, - meeting_id integer NOT NULL + represented_user_id integer ); @@ -1575,7 +1574,6 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, (select array_agg(a.id ORDER BY a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_id = m.id) as personal_note_ids, @@ -2010,7 +2008,6 @@ ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALL ALTER TABLE vote_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; ALTER TABLE vote_t ADD FOREIGN KEY(acting_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE vote_t ADD FOREIGN KEY(represented_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2771,8 +2768,6 @@ CREATE TRIGGER tr_log_vote_t_acting_user_id AFTER INSERT OR UPDATE OF acting_use FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'acting_user_id'); CREATE TRIGGER tr_log_vote_t_represented_user_id AFTER INSERT OR UPDATE OF represented_user_id OR DELETE ON vote_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'represented_user_id'); -CREATE TRIGGER tr_log_vote_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); @@ -3136,7 +3131,6 @@ SQL nt:1rR => meeting/motion_working_group_speaker_ids:-> motion_working_group_s SQL nt:1rR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id SQL nt:1rR => meeting/motion_state_ids:-> motion_state/meeting_id SQL nt:1rR => meeting/poll_ids:-> poll/meeting_id -SQL nt:1rR => meeting/vote_ids:-> vote/meeting_id SQL nt:1rR => meeting/assignment_ids:-> assignment/meeting_id SQL nt:1rR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id @@ -3344,7 +3338,6 @@ FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids FIELD 1rR:nt => vote/poll_id:-> poll/vote_ids FIELD 1r:nt => vote/acting_user_id:-> user/acting_vote_ids FIELD 1r:nt => vote/represented_user_id:-> user/represented_vote_ids -FIELD 1rR:nt => vote/meeting_id:-> meeting/vote_ids SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id diff --git a/models.yml b/models.yml index 9a531879..0c5ab944 100644 --- a/models.yml +++ b/models.yml @@ -1787,11 +1787,6 @@ meeting: to: poll/meeting_id on_delete: CASCADE restriction_mode: B - vote_ids: - type: relation-list - to: vote/meeting_id - on_delete: CASCADE - restriction_mode: B assignment_ids: type: relation-list to: assignment/meeting_id @@ -3540,13 +3535,6 @@ vote: reference: user to: user/represented_vote_ids restriction_mode: B - meeting_id: - type: relation - reference: meeting - to: meeting/vote_ids - required: true - restriction_mode: A - constant: true assignment: id: *id_field From 4f1242911a3f405cdbdd29b07228eb97c5e934b0 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 4 Oct 2025 19:26:59 +0200 Subject: [PATCH 115/142] Move vote/*_user_id in its own restriction mode --- dev/sql/schema_relational.sql | 2 +- models.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index d9ca0106..6992bd37 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '96a677040d25084cdd90c3408371672e' +-- MODELS_YML_CHECKSUM = '522c6c4fea0cb98fe471637bdf3af48f' -- Database parameters diff --git a/models.yml b/models.yml index 0c5ab944..5352e560 100644 --- a/models.yml +++ b/models.yml @@ -3529,12 +3529,12 @@ vote: type: relation reference: user to: user/acting_vote_ids - restriction_mode: B + restriction_mode: C represented_user_id: type: relation reference: user to: user/represented_vote_ids - restriction_mode: B + restriction_mode: C assignment: id: *id_field From 77f9928d9ddaa4a8cf23701cb7487666b0c5f515 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 5 Oct 2025 08:51:16 +0200 Subject: [PATCH 116/142] Rename values of poll/method --- dev/sql/schema_relational.sql | 4 ++-- models.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 6992bd37..a4e50909 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '522c6c4fea0cb98fe471637bdf3af48f' +-- MODELS_YML_CHECKSUM = '1246e2a9233055abd915949df05023c3' -- Database parameters @@ -955,7 +955,7 @@ comment on column motion_workflow_t.sequential_number is 'The (positive) serial CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, - method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('motion', 'selection', 'rating', 'rating-motion')), + method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('approval', 'selection', 'rating-score', 'rating-approval')), config text, visibility varchar(256) NOT NULL CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished')) DEFAULT 'created', diff --git a/models.yml b/models.yml index 5352e560..0a976ef6 100644 --- a/models.yml +++ b/models.yml @@ -3414,10 +3414,10 @@ poll: type: string required: true enum: - - motion + - approval - selection - - rating - - rating-motion + - rating-score + - rating-approval restriction_mode: A config: type: text From d512fa2e203704eb6f86d030369e79224104cbef Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 8 Oct 2025 14:48:56 +0200 Subject: [PATCH 117/142] vote_split --- dev/sql/schema_relational.sql | 7 ++++++- models.yml | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index a4e50909..88b00d36 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '1246e2a9233055abd915949df05023c3' +-- MODELS_YML_CHECKSUM = 'd8f710ff7cfd672ef69e72f451ed558c' -- Database parameters @@ -566,6 +566,7 @@ This email was generated automatically.', poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', poll_default_live_voting_enabled boolean DEFAULT False, poll_default_allow_invalid boolean DEFAULT False, + poll_default_allow_vote_split boolean DEFAULT False, poll_couple_countdown boolean DEFAULT True, logo_projector_main_id integer UNIQUE, logo_projector_header_id integer UNIQUE, @@ -601,6 +602,7 @@ comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.published'' before finished option suggested to user. Is not used in the validations.'; comment on column meeting_t.poll_default_allow_invalid is 'Defines defaut `poll.allow_invalid` option suggested to user.'; +comment on column meeting_t.poll_default_allow_vote_split is 'Defines defaut `poll.allow_vote_split` option suggested to user.'; CREATE TABLE structure_level_t ( @@ -962,6 +964,7 @@ CREATE TABLE poll_t ( result text, published boolean DEFAULT False, allow_invalid boolean DEFAULT False, + allow_vote_split boolean DEFAULT False, sequential_number integer NOT NULL, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -977,12 +980,14 @@ comment on column poll_t.config is 'Values to configure the poll. Depends on the comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually.'; comment on column poll_t.published is 'If true, users can see the result.'; comment on column poll_t.allow_invalid is 'If true, the vote service does not validate. This is always the case for secret polls.'; +comment on column poll_t.allow_vote_split is 'If true, users can split there vote.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight decimal(16,6) DEFAULT '1.000000', + split boolean DEFAULT False, value text, poll_id integer NOT NULL, acting_user_id integer, diff --git a/models.yml b/models.yml index 0a976ef6..514c5eb7 100644 --- a/models.yml +++ b/models.yml @@ -1640,6 +1640,11 @@ meeting: default: false description: Defines defaut `poll.allow_invalid` option suggested to user. restriction_mode: B + poll_default_allow_vote_split: + type: boolean + default: false + description: Defines defaut `poll.allow_vote_split` option suggested to user. + restriction_mode: B poll_couple_countdown: type: boolean default: True @@ -3454,6 +3459,11 @@ poll: default: false description: If true, the vote service does not validate. This is always the case for secret polls. restriction_mode: A + allow_vote_split: + type: boolean + default: false + description: If true, users can split there vote. + restriction_mode: A sequential_number: type: number description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. @@ -3513,6 +3523,10 @@ vote: restriction_mode: B constant: true default: "1.000000" + split: + type: boolean + default: false + restriction_mode: B value: type: text restriction_mode: B From 3126139122879aa3710aef6a7b03349f03585427 Mon Sep 17 00:00:00 2001 From: Kasimir Klinger <77665830+LinKaKling@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:51:59 +0200 Subject: [PATCH 118/142] Fix related Models Notification to be always 'update' (#334) * Fix related Models Notification to be allways 'update' * Fix poll in test_data.sql --- dev/sql/schema_relational.sql | 4 +--- dev/sql/test_data.sql | 9 ++++----- dev/src/generate_sql_schema.py | 4 +--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 88b00d36..d5cb7d99 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -91,14 +91,12 @@ $notify_trigger$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION log_modified_related_models() RETURNS trigger AS $log_modified_related_trigger$ DECLARE - operation_var TEXT; fqid_var TEXT; ref_column TEXT; foreign_table TEXT; foreign_id TEXT; i INTEGER := 0; BEGIN - operation_var := LOWER(TG_OP); WHILE i < TG_NARGS LOOP foreign_table := TG_ARGV[i]; @@ -113,7 +111,7 @@ BEGIN IF foreign_id IS NOT NULL THEN fqid_var := foreign_table || '/' || foreign_id; INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) - VALUES (operation_var, fqid_var, pg_current_xact_id(), now()) + VALUES ('update', fqid_var, pg_current_xact_id(), now()) ON CONFLICT (operation,fqid,xact_id) DO NOTHING; END IF; diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index f55727a2..0981cfd8 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -93,15 +93,14 @@ COMMIT; INSERT INTO poll_t ( id, title, - type, - backend, - pollmethod, - onehundred_percent_base, + method, + visibility, + state, sequential_number, content_object_id, meeting_id ) -VALUES (1, 'Titel1', 'analog', 'fast', 'YNA', 'disabled', 1, 'topic/1', 2); +VALUES (1, 'Titel1', 'selection', 'open', 'created', 1, 'topic/1', 2); SELECT nextval('poll_t_id_seq'); --rl:rl committee_ids:user_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index dda5d0e4..64d633f5 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -817,14 +817,12 @@ class Helper: CREATE OR REPLACE FUNCTION log_modified_related_models() RETURNS trigger AS $log_modified_related_trigger$ DECLARE - operation_var TEXT; fqid_var TEXT; ref_column TEXT; foreign_table TEXT; foreign_id TEXT; i INTEGER := 0; BEGIN - operation_var := LOWER(TG_OP); WHILE i < TG_NARGS LOOP foreign_table := TG_ARGV[i]; @@ -839,7 +837,7 @@ class Helper: IF foreign_id IS NOT NULL THEN fqid_var := foreign_table || '/' || foreign_id; INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) - VALUES (operation_var, fqid_var, pg_current_xact_id(), now()) + VALUES ('update', fqid_var, pg_current_xact_id(), now()) ON CONFLICT (operation,fqid,xact_id) DO NOTHING; END IF; From 882048c59329255b1f2d0343845f38309047539f Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 17 Oct 2025 16:57:38 +0200 Subject: [PATCH 119/142] Move poll/voted_ids to restriction_mode B --- dev/sql/schema_relational.sql | 2 +- models.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index d5cb7d99..01c717d6 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'd8f710ff7cfd672ef69e72f451ed558c' +-- MODELS_YML_CHECKSUM = '61012eeafa6bc4616939f5a7f4fa4e8d' -- Database parameters diff --git a/models.yml b/models.yml index 514c5eb7..a1a65da6 100644 --- a/models.yml +++ b/models.yml @@ -3496,7 +3496,7 @@ poll: voted_ids: type: relation-list to: user/poll_voted_ids - restriction_mode: A + restriction_mode: B entitled_group_ids: type: relation-list to: group/poll_ids From eb7e9f2208ad4afbeaed4363c0c8ffd281057860 Mon Sep 17 00:00:00 2001 From: Raimund Renkert Date: Thu, 23 Oct 2025 18:09:13 +0200 Subject: [PATCH 120/142] Remove setting log_min_messages (#335) --- dev/sql/schema_relational.sql | 11 ----------- dev/src/generate_sql_schema.py | 13 ------------- 2 files changed, 24 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 01c717d6..8e9dfc2d 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -4,17 +4,6 @@ -- MODELS_YML_CHECKSUM = '61012eeafa6bc4616939f5a7f4fa4e8d' --- Database parameters - --- Do not log messages lower than WARNING --- For client side logging this can be overwritten using --- --- SET client_min_messages TO NOTICE; --- --- to get the log messages in the client locally. -SET log_min_messages TO WARNING; - - -- Function and meta table definitions CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 64d633f5..b604be82 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -730,17 +730,6 @@ class Helper: -- Code generated. DO NOT EDIT. """ ) - FILE_TEMPLATE_PARAMETERS = dedent( - """ - -- Do not log messages lower than WARNING - -- For client side logging this can be overwritten using - -- - -- SET client_min_messages TO NOTICE; - -- - -- to get the log messages in the client locally. - SET log_min_messages TO WARNING; - """ - ) FILE_TEMPLATE_CONSTANT_DEFINITIONS = dedent( """ CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine @@ -1481,8 +1470,6 @@ def main() -> None: with open(DESTINATION, "w") as dest: dest.write(Helper.FILE_TEMPLATE_HEADER) dest.write("-- MODELS_YML_CHECKSUM = " + repr(checksum) + "\n") - dest.write("\n\n-- Database parameters\n") - dest.write(Helper.FILE_TEMPLATE_PARAMETERS) dest.write("\n\n-- Function and meta table definitions\n") dest.write(Helper.FILE_TEMPLATE_CONSTANT_DEFINITIONS) dest.write("\n\n-- Type definitions\n") From 54491cca156071401f742e40bc71fc1e89975dcc Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Wed, 29 Oct 2025 11:58:18 +0100 Subject: [PATCH 121/142] [rel-db] add sequential numbers before insert (#319) --- dev/sql/base_data.sql | 7 +- dev/sql/schema_relational.sql | 105 +++++++++++++++++++++++++++- dev/sql/test_data.sql | 15 ++-- dev/src/generate_sql_schema.py | 74 +++++++++++++++++++- dev/src/validate.py | 11 +++ dev/tests/base.py | 4 -- dev/tests/test_generic_relations.py | 8 +-- models.yml | 13 +++- 8 files changed, 210 insertions(+), 27 deletions(-) diff --git a/dev/sql/base_data.sql b/dev/sql/base_data.sql index 00557c22..256fe692 100644 --- a/dev/sql/base_data.sql +++ b/dev/sql/base_data.sql @@ -62,12 +62,11 @@ INSERT INTO motion_workflow_t ( id, name, - sequential_number, first_state_id, meeting_id ) VALUES -(1, 'Simple Workflow', 1, 1, 1); +(1, 'Simple Workflow', 1, 1); -- Increase sequence of motion_workflow_t.id to avoid errors. SELECT nextval('motion_workflow_t_id_seq'); @@ -146,7 +145,6 @@ INSERT INTO projector_t ( id, - sequential_number, meeting_id, used_as_default_projector_for_agenda_item_list_in_meeting_id, used_as_default_projector_for_topic_in_meeting_id, @@ -163,6 +161,7 @@ projector_t used_as_default_projector_for_motion_poll_in_meeting_id, used_as_default_projector_for_poll_in_meeting_id ) -VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); +VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); +SELECT nextval('projector_t_id_seq'); COMMIT; diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 8e9dfc2d..b3f13eca 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,13 +1,51 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '61012eeafa6bc4616939f5a7f4fa4e8d' +-- MODELS_YML_CHECKSUM = '3186e677307ee4e486370173899de032' -- Function and meta table definitions CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine +CREATE FUNCTION generate_sequence() +RETURNS trigger +AS $sequences_trigger$ +-- Creates a sequence for the id given by depend_field NEW data if it doesn't exist. +-- Writes the next value to for this sequence to NEW. +-- In case a number is given in actual_column of the NEW record that is used +-- and the corresponding sequence increased if necessary. +-- Usage with 3 parameters IN TRIGGER DEFINITION: +-- table_name: table this is treated for +-- actual_column: column that will be filled with the actual value +-- depend_field: field that differentiates the sequences. usually meeting_id +DECLARE + table_name TEXT := TG_ARGV[0]; + actual_column TEXT := TG_ARGV[1]; + depend_field TEXT := TG_ARGV[2]; + depend_field_id INTEGER; + sequence_name TEXT; + sequence_value INTEGER; + sequence_max INTEGER; +BEGIN + depend_field_id := hstore(NEW) -> (depend_field); + sequence_name := table_name || '_' || depend_field || depend_field_id || '_' || actual_column || '_seq'; + EXECUTE format('CREATE SEQUENCE IF NOT EXISTS %I OWNED BY %I.%I', sequence_name, table_name, actual_column); + sequence_value := hstore(NEW) -> actual_column; + IF sequence_value IS NULL THEN + sequence_value := nextval(sequence_name); + ELSE + EXECUTE format('SELECT last_value FROM %I', sequence_name) INTO sequence_max; + -- <= because the unused sequence starts with last_value=1 and is_called=f and needs to be written to. + IF sequence_max <= sequence_value THEN + SELECT setval(sequence_name, sequence_value) INTO sequence_value; + END IF; + END IF; + RETURN populate_record(NEW, format('%s=>%s',actual_column, sequence_value)::hstore); +END; +$sequences_trigger$ +LANGUAGE plpgsql; + CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; @@ -677,6 +715,7 @@ CREATE TABLE list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, closed boolean DEFAULT False, sequential_number integer NOT NULL, + CONSTRAINT unique_list_of_speakers_sequential_number UNIQUE (sequential_number, meeting_id), moderator_notes text, content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -749,6 +788,7 @@ CREATE TABLE topic_t ( title varchar(256) NOT NULL, text text, sequential_number integer NOT NULL, + CONSTRAINT unique_topic_sequential_number UNIQUE (sequential_number, meeting_id), meeting_id integer NOT NULL ); @@ -762,6 +802,7 @@ CREATE TABLE motion_t ( number varchar(256), number_value integer, sequential_number integer NOT NULL, + CONSTRAINT unique_motion_sequential_number UNIQUE (sequential_number, meeting_id), title varchar(256) NOT NULL, text text, text_hash varchar(256), @@ -846,6 +887,7 @@ CREATE TABLE motion_comment_section_t ( name varchar(256) NOT NULL, weight integer DEFAULT 10000, sequential_number integer NOT NULL, + CONSTRAINT unique_motion_comment_section_sequential_number UNIQUE (sequential_number, meeting_id), submitter_can_write boolean, meeting_id integer NOT NULL ); @@ -862,6 +904,7 @@ CREATE TABLE motion_category_t ( weight integer DEFAULT 10000, level integer, sequential_number integer NOT NULL, + CONSTRAINT unique_motion_category_sequential_number UNIQUE (sequential_number, meeting_id), parent_id integer, meeting_id integer NOT NULL ); @@ -877,6 +920,7 @@ CREATE TABLE motion_block_t ( title varchar(256) NOT NULL, internal boolean, sequential_number integer NOT NULL, + CONSTRAINT unique_motion_block_sequential_number UNIQUE (sequential_number, meeting_id), meeting_id integer NOT NULL ); @@ -932,6 +976,7 @@ CREATE TABLE motion_workflow_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, sequential_number integer NOT NULL, + CONSTRAINT unique_motion_workflow_sequential_number UNIQUE (sequential_number, meeting_id), first_state_id integer NOT NULL UNIQUE, meeting_id integer NOT NULL ); @@ -953,6 +998,7 @@ CREATE TABLE poll_t ( allow_invalid boolean DEFAULT False, allow_vote_split boolean DEFAULT False, sequential_number integer NOT NULL, + CONSTRAINT unique_poll_sequential_number UNIQUE (sequential_number, meeting_id), content_object_id varchar(100) NOT NULL, content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, @@ -993,6 +1039,7 @@ CREATE TABLE assignment_t ( default_poll_description text, number_poll_candidates boolean, sequential_number integer NOT NULL, + CONSTRAINT unique_assignment_sequential_number UNIQUE (sequential_number, meeting_id), meeting_id integer NOT NULL ); @@ -1091,6 +1138,7 @@ CREATE TABLE projector_t ( show_logo boolean DEFAULT True, show_clock boolean DEFAULT True, sequential_number integer NOT NULL, + CONSTRAINT unique_projector_sequential_number UNIQUE (sequential_number, meeting_id), used_as_default_projector_for_agenda_item_list_in_meeting_id integer, used_as_default_projector_for_topic_in_meeting_id integer, used_as_default_projector_for_list_of_speakers_in_meeting_id integer, @@ -2073,6 +2121,59 @@ ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) +-- Create triggers generating partitioned sequences + +-- definition trigger generate partitioned sequence number for list_of_speakers_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_list_of_speakers_sequential_number BEFORE INSERT ON list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('list_of_speakers_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for topic_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_topic_sequential_number BEFORE INSERT ON topic_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('topic_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for motion_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_sequential_number BEFORE INSERT ON motion_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for motion_comment_section_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_comment_section_sequential_number BEFORE INSERT ON motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_comment_section_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for motion_category_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_category_sequential_number BEFORE INSERT ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_category_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for motion_block_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_block_sequential_number BEFORE INSERT ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_block_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for motion_workflow_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_workflow_sequential_number BEFORE INSERT ON motion_workflow_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_workflow_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for poll_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_poll_sequential_number BEFORE INSERT ON poll_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('poll_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for assignment_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_assignment_sequential_number BEFORE INSERT ON assignment_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('assignment_t', 'sequential_number', 'meeting_id'); + + +-- definition trigger generate partitioned sequence number for projector_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_projector_sequential_number BEFORE INSERT ON projector_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('projector_t', 'sequential_number', 'meeting_id'); + + + -- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships -- definition trigger not null for topic.agenda_item_id against agenda_item.content_object_id_topic_id @@ -3434,4 +3535,4 @@ FIELD 1r:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids */ -/* Missing attribute handling for constant, on_delete, equal_fields, unique, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, on_delete, equal_fields, deferred */ \ No newline at end of file diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index 0981cfd8..e96fb151 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -10,9 +10,9 @@ VALUES (5, 'motionState5', 1, 2, 2); SELECT nextval('motion_state_t_id_seq'); INSERT INTO motion_workflow_t ( - id, name, sequential_number, first_state_id, meeting_id + id, name, first_state_id, meeting_id ) -VALUES (2, 'workflow2', 2, 4, 2); +VALUES (2, 'workflow2', 4, 2); SELECT nextval('motion_workflow_t_id_seq'); INSERT INTO meeting_t ( @@ -40,7 +40,6 @@ SELECT nextval('committee_t_id_seq'); INSERT INTO projector_t ( id, - sequential_number, meeting_id, used_as_default_projector_for_agenda_item_list_in_meeting_id, used_as_default_projector_for_topic_in_meeting_id, @@ -57,7 +56,11 @@ INSERT INTO projector_t ( used_as_default_projector_for_motion_poll_in_meeting_id, used_as_default_projector_for_poll_in_meeting_id ) -VALUES (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); +VALUES (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); +SELECT nextval('projector_t_id_seq'); + +INSERT INTO projector_t (id, meeting_id) +VALUES (3, 2); SELECT nextval('projector_t_id_seq'); INSERT INTO group_t (id, name, meeting_id) @@ -74,8 +77,8 @@ INSERT INTO gm_organization_tag_tagged_ids_t (organization_tag_id, tagged_id) VALUES (2, 'meeting/1'); BEGIN; -INSERT INTO topic_t (id, title, sequential_number, meeting_id) -VALUES (1, 'Thema1', 1, 2); +INSERT INTO topic_t (id, title, meeting_id) +VALUES (1, 'Thema1', 2); SELECT nextval('topic_t_id_seq'); --list_of_speakers.content_object_id:topic.list_of_speakers_id gr:r diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index b604be82..3606f304 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -32,6 +32,7 @@ class SchemaZoneTexts(TypedDict, total=False): post_view: str alter_table: str alter_table_final: str + create_trigger_partitioned_sequences: str create_trigger_1_1_relation_not_null: str create_trigger_relationlistnotnull: str create_trigger_unique_ids_pair_code: str @@ -75,7 +76,9 @@ class GenerateCodeBlocks: @classmethod def generate_the_code( cls, - ) -> tuple[str, str, str, str, str, list[str], str, str, str, str, str, list[str]]: + ) -> tuple[ + str, str, str, str, str, list[str], str, str, str, str, str, str, list[str] + ]: """ Return values: pre_code: Type definitions etc., which should all appear before first table definitions @@ -87,6 +90,7 @@ def generate_the_code( im_table_code: Code for intermediate tables. n:m-relations name schema: f"nm_{smaller-table-name}_{it's-fieldname}_{greater-table_name}" uses one per relation g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field + create_trigger_partitioned_sequences_code: Definitions of triggers calling generate_sequence create_trigger_1_1_relation_not_null_code: Definitions of triggers calling check_not_null_for_1_1_relation create_trigger_relationlistnotnull_code: Definitions of triggers calling check_not_null_for_relation_lists create_trigger_unique_ids_pair_code: Definitions of triggers calling check_unique_ids_pair @@ -108,6 +112,7 @@ def generate_the_code( "items", "to", "reference", + "sequence_scope", # "on_delete", # must have other name then the key-value-store one "sql", # "equal_fields", # Seems we need, see example_transactional.sql between meeting and groups? @@ -117,6 +122,7 @@ def generate_the_code( table_name_code: str = "" view_name_code: str = "" alter_table_final_code: str = "" + create_trigger_partitioned_sequences_code: str = "" create_trigger_1_1_relation_not_null_code: str = "" create_trigger_relationlistnotnull_code: str = "" create_trigger_unique_ids_pair_code: str = "" @@ -169,6 +175,8 @@ def generate_the_code( view_name_code += code if code := schema_zone_texts["alter_table_final"]: alter_table_final_code += code + "\n" + if code := schema_zone_texts["create_trigger_partitioned_sequences"]: + create_trigger_partitioned_sequences_code += code + "\n" if code := schema_zone_texts["create_trigger_1_1_relation_not_null"]: create_trigger_1_1_relation_not_null_code += code + "\n" if code := schema_zone_texts["create_trigger_relationlistnotnull"]: @@ -199,6 +207,7 @@ def generate_the_code( final_info_code, missing_handled_attributes, im_table_code, + create_trigger_partitioned_sequences_code, create_trigger_1_1_relation_not_null_code, create_trigger_relationlistnotnull_code, create_trigger_unique_ids_pair_code, @@ -243,6 +252,15 @@ def get_schema_simple_types( ) -> tuple[SchemaZoneTexts, str]: text, subst = cls.get_text_for_simple_types(table_name, fname, fdata, type_) text["table"] = Helper.FIELD_TEMPLATE.substitute(subst) + if depend_field := fdata.get("sequence_scope"): + text[ + "create_trigger_partitioned_sequences" + ] += cls.get_trigger_generate_partitioned_sequence( + table_name, fname, depend_field + ) + text[ + "table" + ] += f" CONSTRAINT unique_{table_name}_{fname} UNIQUE ({fname}, {depend_field}),\n" return text, "" @classmethod @@ -562,6 +580,19 @@ def get_sql_for_relation_n_1( query = f"select array_cat(({arr1}), ({arr2}))" return f"({query}) as {fname},\n" + @classmethod + def get_trigger_generate_partitioned_sequence( + cls, view_name: str, actual_field: str, depend_field: str + ) -> str: + table_name = HelperGetNames.get_table_name(view_name) + return dedent( + f""" + -- definition trigger generate partitioned sequence number for {table_name}.{actual_field} partitioned by {depend_field} + CREATE TRIGGER tr_generate_sequence_{view_name}_{actual_field} BEFORE INSERT ON {table_name} + FOR EACH ROW EXECUTE FUNCTION generate_sequence('{table_name}', '{actual_field}', '{depend_field}'); + """ + ) + @classmethod def get_trigger_check_not_null_for_1_1_relation( cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str @@ -734,6 +765,44 @@ class Helper: """ CREATE EXTENSION hstore; -- included in standard postgres-installations, check for alpine + CREATE FUNCTION generate_sequence() + RETURNS trigger + AS $sequences_trigger$ + -- Creates a sequence for the id given by depend_field NEW data if it doesn't exist. + -- Writes the next value to for this sequence to NEW. + -- In case a number is given in actual_column of the NEW record that is used + -- and the corresponding sequence increased if necessary. + -- Usage with 3 parameters IN TRIGGER DEFINITION: + -- table_name: table this is treated for + -- actual_column: column that will be filled with the actual value + -- depend_field: field that differentiates the sequences. usually meeting_id + DECLARE + table_name TEXT := TG_ARGV[0]; + actual_column TEXT := TG_ARGV[1]; + depend_field TEXT := TG_ARGV[2]; + depend_field_id INTEGER; + sequence_name TEXT; + sequence_value INTEGER; + sequence_max INTEGER; + BEGIN + depend_field_id := hstore(NEW) -> (depend_field); + sequence_name := table_name || '_' || depend_field || depend_field_id || '_' || actual_column || '_seq'; + EXECUTE format('CREATE SEQUENCE IF NOT EXISTS %I OWNED BY %I.%I', sequence_name, table_name, actual_column); + sequence_value := hstore(NEW) -> actual_column; + IF sequence_value IS NULL THEN + sequence_value := nextval(sequence_name); + ELSE + EXECUTE format('SELECT last_value FROM %I', sequence_name) INTO sequence_max; + -- <= because the unused sequence starts with last_value=1 and is_called=f and needs to be written to. + IF sequence_max <= sequence_value THEN + SELECT setval(sequence_name, sequence_value) INTO sequence_value; + END IF; + END IF; + RETURN populate_record(NEW, format('%s=>%s',actual_column, sequence_value)::hstore); + END; + $sequences_trigger$ + LANGUAGE plpgsql; + CREATE FUNCTION log_modified_models() RETURNS trigger AS $log_modified_trigger$ DECLARE escaped_table_name varchar; @@ -1461,6 +1530,7 @@ def main() -> None: final_info_code, missing_handled_attributes, im_table_code, + create_trigger_partitioned_sequences_code, create_trigger_1_1_relation_not_null_code, create_trigger_relationlistnotnull_code, create_trigger_unique_ids_pair_code, @@ -1482,6 +1552,8 @@ def main() -> None: dest.write(view_name_code) dest.write("\n\n-- Alter table relations\n") dest.write(alter_table_code) + dest.write("\n\n-- Create triggers generating partitioned sequences\n") + dest.write(create_trigger_partitioned_sequences_code) dest.write( "\n\n-- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships\n" ) diff --git a/dev/src/validate.py b/dev/src/validate.py index 20a8104e..e36c6c0c 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -54,6 +54,7 @@ "read_only", "constant", "unique", + "sequence_scope", ) @@ -158,6 +159,16 @@ def check_field( if field.get("calculated"): return + if field_name := field.get("sequence_scope", ""): + if type != "number": + self.errors.append( + f"Sequences can only be generated for number fields. {collectionfield} is {type}." + ) + if field_name not in self.models[collection]: + self.errors.append( + f"{field_name} can not be used as a source of sequence scope since it is not part of {collection}." + ) + valid_attributes = list(OPTIONAL_ATTRIBUTES) + required_attributes if type == "string[]": valid_attributes.append("items") diff --git a/dev/tests/base.py b/dev/tests/base.py index 482e7227..c108ab80 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -341,7 +341,6 @@ def create_meeting( "show_title": True, "show_logo": True, "show_clock": True, - "sequential_number": 1, "used_as_default_projector_for_agenda_item_list_in_meeting_id": result[ "meeting_id" ], @@ -405,7 +404,6 @@ def create_meeting( "show_title": True, "show_logo": True, "show_clock": True, - "sequential_number": 2, "meeting_id": result["meeting_id"], }, ] @@ -781,14 +779,12 @@ def create_meeting( { "id": result["simple_workflow_id"], "name": "Simple Workflow", - "sequential_number": 1, "first_state_id": wf_m1_simple_first_state_id, "meeting_id": result["meeting_id"], }, { "id": result["complex_workflow_id"], "name": "Complex Workflow", - "sequential_number": 2, "first_state_id": wf_m1_complex_first_state_id, "meeting_id": result["meeting_id"], }, diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 1a0e6b08..099986a3 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -79,10 +79,9 @@ def test_o2o_generic_1t_1GrR_okay(self) -> None: *assignment_t.insert( [ assignment_t.title, - assignment_t.sequential_number, assignment_t.meeting_id, ], - [["title assignment 1", 21, self.meeting1_id]], + [["title assignment 1", self.meeting1_id]], returning=[assignment_t.id], ) ).fetchone()["id"] @@ -148,7 +147,6 @@ def test_o2o_generic_1t_1GrR_okay_with_setval(self) -> None: content_object_id := f"mediafile/{mediafile_id}" ), "meeting_id": self.meeting1_id, - "sequential_number": 28, }, ] columns, values = DbUtils.get_columns_and_values_for_insert( @@ -381,7 +379,6 @@ def test_generic_1tR_1GrR_okay(self) -> None: { "id": assignment_id, "title": "I am an assignment", - "sequential_number": 42, "meeting_id": self.meeting1_id, }, ], @@ -408,7 +405,6 @@ def test_generic_1tR_1GrR_okay(self) -> None: content_object_id := f"assignment/{assignment_id}" ), "meeting_id": self.meeting1_id, - "sequential_number": 28, } ], ) @@ -788,7 +784,6 @@ def test_o2m_ntR_1r_insert_delete_okay(self) -> None: "used_as_default_projector_for_assignment_poll_in_meeting_id", "used_as_default_projector_for_motion_poll_in_meeting_id", "used_as_default_projector_for_poll_in_meeting_id", - "sequential_number", ], ) projector: dict[str, Any] = curs.execute( @@ -799,7 +794,6 @@ def test_o2m_ntR_1r_insert_delete_okay(self) -> None: ) ).fetchone() projector_old_id = projector.pop("id") - projector["sequential_number"] += 2 columns, values = DbUtils.get_columns_and_values_for_insert( projector_t, [projector] ) diff --git a/models.yml b/models.yml index a1a65da6..2b77ed7f 100644 --- a/models.yml +++ b/models.yml @@ -97,8 +97,6 @@ # fields the value as to be an id of an existing object. # - The property `equal_fields` describes fields that must have the same value in # the instance and the related instance. -# - The property `unique` will guarantee the uniqueness of fields or field combinations by database constraint.. -# Example: `unique: meeting_id, sequential_number` # Restriction Mode: # The field `restriction_mode` is required for every field. It puts the field into a # restriction group. See https://github.com/OpenSlides/OpenSlides/wiki/Restrictions-Overview @@ -2386,6 +2384,7 @@ list_of_speakers: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -2599,6 +2598,7 @@ topic: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -2658,12 +2658,12 @@ motion: restriction_mode: D sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true restriction_mode: C constant: true - unique: meeting_id, sequential_number title: type: string required: true @@ -3037,6 +3037,7 @@ motion_comment_section: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3090,6 +3091,7 @@ motion_category: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3131,6 +3133,7 @@ motion_block: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3374,6 +3377,7 @@ motion_workflow: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3466,6 +3470,7 @@ poll: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3580,6 +3585,7 @@ assignment: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true @@ -3981,6 +3987,7 @@ projector: restriction_mode: A sequential_number: type: number + sequence_scope: meeting_id description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. read_only: true required: true From acdf857af241fa6bc65bf51092b59dfcb22243bf Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 3 Nov 2025 17:08:05 +0100 Subject: [PATCH 122/142] [rel-db] Add poll_config_X-collections (#338) * Add poll_config_X-collections --- dev/sql/schema_relational.sql | 264 ++++++++++++++++++++++++++++------ dev/sql/test_data.sql | 14 -- dev/src/helper_get_names.py | 6 +- models.yml | 202 +++++++++++++++++++++----- 4 files changed, 392 insertions(+), 94 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index b3f13eca..6f9c7b5b 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '3186e677307ee4e486370173899de032' +-- MODELS_YML_CHECKSUM = 'ebb63fc6d10ae8ef886d92a23ea03f55' -- Function and meta table definitions @@ -989,8 +989,12 @@ comment on column motion_workflow_t.sequential_number is 'The (positive) serial CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, - method varchar(256) NOT NULL CONSTRAINT enum_poll_method CHECK (method IN ('approval', 'selection', 'rating-score', 'rating-approval')), - config text, + config_id varchar(100) NOT NULL, + config_id_poll_config_approval_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_approval' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + config_id_poll_config_selection_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_selection' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + config_id_poll_config_rating_score_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_rating_score' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + config_id_poll_config_rating_approval_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_rating_approval' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_config_id_part1 CHECK (split_part(config_id, '/', 1) IN ('poll_config_approval','poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), visibility varchar(256) NOT NULL CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished')) DEFAULT 'created', result text, @@ -1009,7 +1013,6 @@ CREATE TABLE poll_t ( -comment on column poll_t.config is 'Values to configure the poll. Depends on the value in poll/method.'; comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually.'; comment on column poll_t.published is 'If true, users can see the result.'; comment on column poll_t.allow_invalid is 'If true, the vote service does not validate. This is always the case for secret polls.'; @@ -1017,14 +1020,73 @@ comment on column poll_t.allow_vote_split is 'If true, users can split there vot comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE vote_t ( +CREATE TABLE poll_config_approval_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_id integer NOT NULL UNIQUE, + allow_abstain boolean DEFAULT True +); + + + + +CREATE TABLE poll_config_selection_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_id integer NOT NULL UNIQUE, + max_options_amount integer DEFAULT 0, + min_options_amount integer DEFAULT 0, + allow_nota boolean DEFAULT False +); + + + + +CREATE TABLE poll_config_rating_score_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_id integer NOT NULL UNIQUE, + max_options_amount integer DEFAULT 0, + min_options_amount integer DEFAULT 0, + max_votes_per_option integer DEFAULT 0, + max_vote_sum integer DEFAULT 0, + min_vote_sum integer DEFAULT 0 +); + + + + +CREATE TABLE poll_config_rating_approval_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_id integer NOT NULL UNIQUE, + max_options_amount integer DEFAULT 0, + min_options_amount integer DEFAULT 0, + allow_abstain boolean DEFAULT True +); + + + + +CREATE TABLE poll_config_option_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_config_id varchar(100) NOT NULL, + poll_config_id_poll_config_selection_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_selection' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + poll_config_id_poll_config_rating_score_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_score' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + poll_config_id_poll_config_rating_approval_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_approval' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_poll_config_id_part1 CHECK (split_part(poll_config_id, '/', 1) IN ('poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), + weight integer, + text varchar(256), + meeting_user_id integer +); + + + + +CREATE TABLE ballot_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight decimal(16,6) DEFAULT '1.000000', split boolean DEFAULT False, value text, poll_id integer NOT NULL, - acting_user_id integer, - represented_user_id integer + acting_meeting_user_id integer, + represented_meeting_user_id integer ); @@ -1295,6 +1357,12 @@ CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( PRIMARY KEY (meeting_user_id, motion_id) ); +CREATE TABLE nm_meeting_user_poll_voted_ids_poll_t ( + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (meeting_user_id, poll_id) +); + CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1418,12 +1486,6 @@ CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( PRIMARY KEY (next_state_id, previous_state_id) ); -CREATE TABLE nm_poll_voted_ids_user_t ( - poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (poll_id, user_id) -); - CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1493,9 +1555,6 @@ CREATE VIEW "user" AS SELECT *, , (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.acting_user_id = u.id) as acting_vote_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.represented_user_id = u.id) as represented_vote_ids, (select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, (select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, (select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, @@ -1520,6 +1579,10 @@ CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, (select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_meeting_user_poll_voted_ids_poll_t n where n.meeting_user_id = m.id) as poll_voted_ids, +(select array_agg(p.id ORDER BY p.id) from poll_config_option_t p where p.meeting_user_id = m.id) as poll_option_ids, +(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.acting_meeting_user_id = m.id) as acting_ballot_ids, +(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.represented_meeting_user_id = m.id) as represented_ballot_ids, (select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, (select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, (select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids @@ -1800,14 +1863,35 @@ FROM motion_workflow_t m; CREATE VIEW "poll" AS SELECT *, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.poll_id = p.id) as vote_ids, -(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, +(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.poll_id = p.id) as ballot_ids, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_poll_voted_ids_poll_t n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, (select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; -CREATE VIEW "vote" AS SELECT * FROM vote_t v; +CREATE VIEW "poll_config_approval" AS SELECT * FROM poll_config_approval_t p; + + +CREATE VIEW "poll_config_selection" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_selection_id = p.id) as option_ids +FROM poll_config_selection_t p; + + +CREATE VIEW "poll_config_rating_score" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_rating_score_id = p.id) as option_ids +FROM poll_config_rating_score_t p; + + +CREATE VIEW "poll_config_rating_approval" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_rating_approval_id = p.id) as option_ids +FROM poll_config_rating_approval_t p; + + +CREATE VIEW "poll_config_option" AS SELECT * FROM poll_config_option_t p; + + +CREATE VIEW "ballot" AS SELECT * FROM ballot_t b; CREATE VIEW "assignment" AS SELECT *, @@ -2040,14 +2124,31 @@ ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_approval_id) REFERENCES poll_config_approval_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_selection_id) REFERENCES poll_config_selection_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_rating_score_id) REFERENCES poll_config_rating_score_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_rating_approval_id) REFERENCES poll_config_rating_approval_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(acting_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(represented_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_config_approval_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; + +ALTER TABLE poll_config_selection_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; + +ALTER TABLE poll_config_rating_score_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; + +ALTER TABLE poll_config_rating_approval_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; + +ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_selection_id) REFERENCES poll_config_selection_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_score_id) REFERENCES poll_config_rating_score_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_approval_id) REFERENCES poll_config_rating_approval_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_config_option_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; + +ALTER TABLE ballot_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE ballot_t ADD FOREIGN KEY(acting_meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE ballot_t ADD FOREIGN KEY(represented_meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2377,6 +2478,11 @@ DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_e CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); +CREATE TRIGGER tr_log_nm_meeting_user_poll_voted_ids_poll_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_poll_voted_ids_poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','poll','poll_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_poll_voted_ids_poll_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + CREATE TRIGGER tr_log_nm_meeting_user_structure_level_ids_structure_level_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','structure_level','structure_level_id'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t @@ -2834,6 +2940,18 @@ CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELET DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_config_approval_config_id_poll_config_approval_id AFTER INSERT OR UPDATE OF config_id_poll_config_approval_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_approval','config_id_poll_config_approval_id'); + +CREATE TRIGGER tr_log_poll_config_selection_config_id_poll_config_selection_id AFTER INSERT OR UPDATE OF config_id_poll_config_selection_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_selection','config_id_poll_config_selection_id'); + +CREATE TRIGGER tr_log_poll_config_rating_score_config_id_poll_config_rating_score_id AFTER INSERT OR UPDATE OF config_id_poll_config_rating_score_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_score','config_id_poll_config_rating_score_id'); + +CREATE TRIGGER tr_log_poll_config_rating_approval_config_id_poll_config_rating_approval_id AFTER INSERT OR UPDATE OF config_id_poll_config_rating_approval_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_approval','config_id_poll_config_rating_approval_id'); + CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); @@ -2842,25 +2960,69 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_ CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); - -CREATE TRIGGER tr_log_nm_poll_voted_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','poll_id','user','user_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_vote AFTER INSERT OR UPDATE OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t +CREATE TRIGGER tr_log_poll_config_approval AFTER INSERT OR UPDATE OR DELETE ON poll_config_approval_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_approval'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_approval_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_vote_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON vote_t +CREATE TRIGGER tr_log_poll_config_approval_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_approval_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); -CREATE TRIGGER tr_log_vote_t_acting_user_id AFTER INSERT OR UPDATE OF acting_user_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'acting_user_id'); -CREATE TRIGGER tr_log_vote_t_represented_user_id AFTER INSERT OR UPDATE OF represented_user_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'represented_user_id'); + +CREATE TRIGGER tr_log_poll_config_selection AFTER INSERT OR UPDATE OR DELETE ON poll_config_selection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_selection'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_selection_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_poll_config_selection_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_selection_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); + +CREATE TRIGGER tr_log_poll_config_rating_score AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_score_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_rating_score'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_score_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_poll_config_rating_score_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_rating_score_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); + +CREATE TRIGGER tr_log_poll_config_rating_approval AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_approval_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_rating_approval'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_approval_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_poll_config_rating_approval_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_rating_approval_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); + +CREATE TRIGGER tr_log_poll_config_option AFTER INSERT OR UPDATE OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_option'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_option_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + + +CREATE TRIGGER tr_log_poll_config_selection_poll_config_id_poll_config_selection_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_selection_id OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_selection','poll_config_id_poll_config_selection_id'); + +CREATE TRIGGER tr_log_poll_config_rating_score_poll_config_id_poll_config_rating_score_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_rating_score_id OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_score','poll_config_id_poll_config_rating_score_id'); + +CREATE TRIGGER tr_log_poll_config_rating_approval_poll_config_id_poll_config_rating_approval_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_rating_approval_id OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_approval','poll_config_id_poll_config_rating_approval_id'); +CREATE TRIGGER tr_log_poll_config_option_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); + +CREATE TRIGGER tr_log_ballot AFTER INSERT OR UPDATE OR DELETE ON ballot_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('ballot'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON ballot_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_ballot_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON ballot_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); +CREATE TRIGGER tr_log_ballot_t_acting_meeting_user_id AFTER INSERT OR UPDATE OF acting_meeting_user_id OR DELETE ON ballot_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'acting_meeting_user_id'); +CREATE TRIGGER tr_log_ballot_t_represented_meeting_user_id AFTER INSERT OR UPDATE OF represented_meeting_user_id OR DELETE ON ballot_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'represented_meeting_user_id'); CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); @@ -3142,9 +3304,6 @@ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id -SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids -SQL nt:1r => user/acting_vote_ids:-> vote/acting_user_id -SQL nt:1r => user/represented_vote_ids:-> vote/represented_user_id SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids SQL nt:1r => user/history_position_ids:-> history_position/user_id @@ -3162,6 +3321,10 @@ SQL nt:1rR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id +SQL nt:nt => meeting_user/poll_voted_ids:-> poll/voted_ids +SQL nr:1r => meeting_user/poll_option_ids:-> poll_config_option/meeting_user_id +SQL nt:1r => meeting_user/acting_ballot_ids:-> ballot/acting_meeting_user_id +SQL nt:1r => meeting_user/represented_ballot_ids:-> ballot/represented_meeting_user_id SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids @@ -3421,16 +3584,31 @@ SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_def SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids +FIELD 1GrR:1rR,1rR,1rR,1rR => poll/config_id:-> poll_config_approval/poll_id,poll_config_selection/poll_id,poll_config_rating_score/poll_id,poll_config_rating_approval/poll_id FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids -SQL nt:1rR => poll/vote_ids:-> vote/poll_id -SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids +SQL nr:1rR => poll/ballot_ids:-> ballot/poll_id +SQL nt:nt => poll/voted_ids:-> meeting_user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids -FIELD 1rR:nt => vote/poll_id:-> poll/vote_ids -FIELD 1r:nt => vote/acting_user_id:-> user/acting_vote_ids -FIELD 1r:nt => vote/represented_user_id:-> user/represented_vote_ids +FIELD 1rR:1GrR => poll_config_approval/poll_id:-> poll/config_id + +FIELD 1rR:1GrR => poll_config_selection/poll_id:-> poll/config_id +SQL nt:1GrR => poll_config_selection/option_ids:-> poll_config_option/poll_config_id + +FIELD 1rR:1GrR => poll_config_rating_score/poll_id:-> poll/config_id +SQL nt:1GrR => poll_config_rating_score/option_ids:-> poll_config_option/poll_config_id + +FIELD 1rR:1GrR => poll_config_rating_approval/poll_id:-> poll/config_id +SQL nt:1GrR => poll_config_rating_approval/option_ids:-> poll_config_option/poll_config_id + +FIELD 1GrR:nt,nt,nt => poll_config_option/poll_config_id:-> poll_config_selection/option_ids,poll_config_rating_score/option_ids,poll_config_rating_approval/option_ids +FIELD 1r:nr => poll_config_option/meeting_user_id:-> meeting_user/poll_option_ids + +FIELD 1rR:nr => ballot/poll_id:-> poll/ballot_ids +FIELD 1r:nt => ballot/acting_meeting_user_id:-> meeting_user/acting_ballot_ids +FIELD 1r:nt => ballot/represented_meeting_user_id:-> meeting_user/represented_ballot_ids SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index e96fb151..ac8d2f86 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -92,20 +92,6 @@ INSERT INTO agenda_item_t (content_object_id, meeting_id) VALUES ('topic/1', 2); COMMIT; ---rl:gr topic.poll_ids:poll.content_object_id -INSERT INTO poll_t ( - id, - title, - method, - visibility, - state, - sequential_number, - content_object_id, - meeting_id -) -VALUES (1, 'Titel1', 'selection', 'open', 'created', 1, 'topic/1', 2); -SELECT nextval('poll_t_id_seq'); - --rl:rl committee_ids:user_ids INSERT INTO nm_committee_manager_ids_user_t (committee_id, user_id) VALUES (1, 1); diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 6ce46b6b..15a6555e 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -319,12 +319,12 @@ def check_field_length(cls) -> None: @staticmethod def get_field_definition_from_to(to: str) -> tuple[str, str, dict[str, Any]]: - tname, fname = to.split("/") try: + tname, fname = to.split("/") field = InternalHelper.get_models(tname, fname) - except Exception: + except Exception as e: raise Exception( - f"Exception on splitting to {to} in get_field_definition_from_to" + f"Exception on splitting to {to} in get_field_definition_from_to: {e}" ) assert ( len(tname) <= HelperGetNames.MAX_LEN diff --git a/models.yml b/models.yml index 2b77ed7f..aaaa1b96 100644 --- a/models.yml +++ b/models.yml @@ -427,18 +427,6 @@ user: to: meeting_user/user_id restriction_mode: A on_delete: CASCADE - poll_voted_ids: - type: relation-list - to: poll/voted_ids - restriction_mode: A - acting_vote_ids: - type: relation-list - to: vote/acting_user_id - restriction_mode: A - represented_vote_ids: - type: relation-list - to: vote/represented_user_id - restriction_mode: A poll_candidate_ids: type: relation-list to: poll_candidate/user_id @@ -564,6 +552,24 @@ meeting_user: to: meeting_user/vote_delegated_to_id equal_fields: meeting_id restriction_mode: A + poll_voted_ids: + type: relation-list + to: poll/voted_ids + restriction_mode: A + poll_option_ids: + type: relation-list + to: poll_config_option/meeting_user_id + reference: poll_config_option + required: false + restriction_mode: A + acting_ballot_ids: + type: relation-list + to: ballot/acting_meeting_user_id + restriction_mode: A + represented_ballot_ids: + type: relation-list + to: ballot/represented_meeting_user_id + restriction_mode: A chat_message_ids: type: relation-list to: chat_message/meeting_user_id @@ -3419,18 +3425,19 @@ poll: type: string required: true restriction_mode: A - method: - type: string + config_id: + type: generic-relation + to: + - poll_config_approval/poll_id + - poll_config_selection/poll_id + - poll_config_rating_score/poll_id + - poll_config_rating_approval/poll_id + reference: + - poll_config_approval + - poll_config_selection + - poll_config_rating_score + - poll_config_rating_approval required: true - enum: - - approval - - selection - - rating-score - - rating-approval - restriction_mode: A - config: - type: text - description: Values to configure the poll. Depends on the value in poll/method. restriction_mode: A visibility: type: string @@ -3492,15 +3499,16 @@ poll: restriction_mode: A required: true constant: true - vote_ids: + ballot_ids: type: relation-list - to: vote/poll_id + to: ballot/poll_id + reference: ballot on_delete: CASCADE equal_fields: meeting_id restriction_mode: A voted_ids: type: relation-list - to: user/poll_voted_ids + to: meeting_user/poll_voted_ids restriction_mode: B entitled_group_ids: type: relation-list @@ -3521,7 +3529,133 @@ poll: required: true constant: true -vote: +poll_config_approval: + id: *id_field + poll_id: + type: relation + to: poll/config_id + reference: poll + required: true + restriction_mode: A + allow_abstain: + type: boolean + default: true + restriction_mode: A + +poll_config_selection: + id: *id_field + poll_id: + type: relation + to: poll/config_id + reference: poll + required: true + restriction_mode: A + option_ids: + type: relation-list + to: poll_config_option/poll_config_id + on_delete: CASCADE + restriction_mode: A + max_options_amount: + type: number + default: 0 + restriction_mode: A + min_options_amount: + type: number + default: 0 + restriction_mode: A + allow_nota: + type: boolean + default: false + restriction_mode: A + +poll_config_rating_score: + id: *id_field + poll_id: + type: relation + to: poll/config_id + reference: poll + required: true + restriction_mode: A + option_ids: + type: relation-list + to: poll_config_option/poll_config_id + on_delete: CASCADE + restriction_mode: A + max_options_amount: + type: number + default: 0 + restriction_mode: A + min_options_amount: + type: number + default: 0 + restriction_mode: A + max_votes_per_option: + type: number + default: 0 + restriction_mode: A + max_vote_sum: + type: number + default: 0 + restriction_mode: A + min_vote_sum: + type: number + default: 0 + restriction_mode: A + +poll_config_rating_approval: + id: *id_field + poll_id: + type: relation + to: poll/config_id + reference: poll + required: true + restriction_mode: A + option_ids: + type: relation-list + to: poll_config_option/poll_config_id + on_delete: CASCADE + restriction_mode: A + max_options_amount: + type: number + default: 0 + restriction_mode: A + min_options_amount: + type: number + default: 0 + restriction_mode: A + allow_abstain: + type: boolean + default: true + restriction_mode: A + +poll_config_option: + id: *id_field + poll_config_id: + type: generic-relation + to: + - poll_config_selection/option_ids + - poll_config_rating_score/option_ids + - poll_config_rating_approval/option_ids + reference: + - poll_config_selection + - poll_config_rating_score + - poll_config_rating_approval + required: true + restriction_mode: A + weight: + type: number + restriction_mode: A + text: + type: string + restriction_mode: A + meeting_user_id: + type: relation + to: meeting_user/poll_option_ids + reference: meeting_user + required: false + restriction_mode: A + +ballot: id: *id_field weight: type: decimal(6) @@ -3539,20 +3673,20 @@ vote: poll_id: type: relation reference: poll - to: poll/vote_ids + to: poll/ballot_ids equal_fields: meeting_id required: true restriction_mode: A constant: true - acting_user_id: + acting_meeting_user_id: type: relation - reference: user - to: user/acting_vote_ids + reference: meeting_user + to: meeting_user/acting_ballot_ids restriction_mode: C - represented_user_id: + represented_meeting_user_id: type: relation - reference: user - to: user/represented_vote_ids + reference: meeting_user + to: meeting_user/represented_ballot_ids restriction_mode: C assignment: From a13f31d8b44e531c48e45e75153f16c0ac0fbfd8 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 5 Nov 2025 09:48:23 +0100 Subject: [PATCH 123/142] New way to vote vor a list of candidates or other optons (#341) --- dev/sql/schema_relational.sql | 82 +++++------------------------------ models.yml | 66 +++------------------------- 2 files changed, 19 insertions(+), 129 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 6f9c7b5b..73ff4b4f 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'ebb63fc6d10ae8ef886d92a23ea03f55' +-- MODELS_YML_CHECKSUM = 'a86f14641a8c926d16b692535739655c' -- Function and meta table definitions @@ -1067,10 +1067,11 @@ CREATE TABLE poll_config_rating_approval_t ( CREATE TABLE poll_config_option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, poll_config_id varchar(100) NOT NULL, + poll_config_id_poll_config_approval_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_approval' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, poll_config_id_poll_config_selection_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_selection' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, poll_config_id_poll_config_rating_score_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_score' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, poll_config_id_poll_config_rating_approval_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_approval' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_poll_config_id_part1 CHECK (split_part(poll_config_id, '/', 1) IN ('poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), + CONSTRAINT valid_poll_config_id_part1 CHECK (split_part(poll_config_id, '/', 1) IN ('poll_config_approval','poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), weight integer, text varchar(256), meeting_user_id integer @@ -1121,25 +1122,6 @@ CREATE TABLE assignment_candidate_t ( -CREATE TABLE poll_candidate_list_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - meeting_id integer NOT NULL -); - - - - -CREATE TABLE poll_candidate_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_candidate_list_id integer NOT NULL, - user_id integer, - weight integer NOT NULL, - meeting_id integer NOT NULL -); - - - - CREATE TABLE mediafile_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256), @@ -1555,7 +1537,6 @@ CREATE VIEW "user" AS SELECT *, , (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, (select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, (select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, ( @@ -1644,8 +1625,6 @@ comment on column "committee".user_ids is 'Calculated field: All users which are CREATE VIEW "meeting" AS SELECT *, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, -(select array_agg(p.id ORDER BY p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, -(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, (select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.meeting_id = m.id) as meeting_user_ids, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, @@ -1870,7 +1849,9 @@ CREATE VIEW "poll" AS SELECT *, FROM poll_t p; -CREATE VIEW "poll_config_approval" AS SELECT * FROM poll_config_approval_t p; +CREATE VIEW "poll_config_approval" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_approval_id = p.id) as option_ids +FROM poll_config_approval_t p; CREATE VIEW "poll_config_selection" AS SELECT *, @@ -1909,14 +1890,6 @@ FROM assignment_t a; CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; -CREATE VIEW "poll_candidate_list" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids -FROM poll_candidate_list_t p; - - -CREATE VIEW "poll_candidate" AS SELECT * FROM poll_candidate_t p; - - CREATE VIEW "mediafile" AS SELECT *, (select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, (select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids @@ -2141,6 +2114,7 @@ ALTER TABLE poll_config_rating_score_t ADD FOREIGN KEY(poll_id) REFERENCES poll_ ALTER TABLE poll_config_rating_approval_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_approval_id) REFERENCES poll_config_approval_t(id) INITIALLY DEFERRED; ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_selection_id) REFERENCES poll_config_selection_t(id) INITIALLY DEFERRED; ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_score_id) REFERENCES poll_config_rating_score_t(id) INITIALLY DEFERRED; ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_approval_id) REFERENCES poll_config_rating_approval_t(id) INITIALLY DEFERRED; @@ -2156,12 +2130,6 @@ ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES ass ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -3001,6 +2969,9 @@ CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELET DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_config_approval_poll_config_id_poll_config_approval_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_approval_id OR DELETE ON poll_config_option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_approval','poll_config_id_poll_config_approval_id'); + CREATE TRIGGER tr_log_poll_config_selection_poll_config_id_poll_config_selection_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_selection_id OR DELETE ON poll_config_option_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_selection','poll_config_id_poll_config_selection_id'); @@ -3044,26 +3015,6 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeti CREATE TRIGGER tr_log_assignment_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_poll_candidate_list AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_poll_candidate_list_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_list_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_poll_candidate AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_poll_candidate_t_poll_candidate_list_id AFTER INSERT OR UPDATE OF poll_candidate_list_id OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list', 'poll_candidate_list_id'); -CREATE TRIGGER tr_log_poll_candidate_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); -CREATE TRIGGER tr_log_poll_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - CREATE TRIGGER tr_log_mediafile AFTER INSERT OR UPDATE OR DELETE ON mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t @@ -3304,7 +3255,6 @@ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id -SQL nt:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids SQL nt:1r => user/history_position_ids:-> history_position/user_id SQL nt:1Gr => user/history_entry_ids:-> history_entry/model_id @@ -3354,8 +3304,6 @@ FIELD 1r:nt => meeting/template_for_organization_id:-> organization/template_mee FIELD 1rR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id FIELD 1rR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id SQL nt:1r => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id -SQL nt:1rR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id -SQL nt:1rR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id SQL nt:1rR => meeting/meeting_user_ids:-> meeting_user/meeting_id SQL nt:1r => meeting/assignment_poll_default_group_ids:-> group/used_as_assignment_poll_default_id SQL nt:1r => meeting/poll_default_group_ids:-> group/used_as_poll_default_id @@ -3593,6 +3541,7 @@ SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids FIELD 1rR:1GrR => poll_config_approval/poll_id:-> poll/config_id +SQL nt:1GrR => poll_config_approval/option_ids:-> poll_config_option/poll_config_id FIELD 1rR:1GrR => poll_config_selection/poll_id:-> poll/config_id SQL nt:1GrR => poll_config_selection/option_ids:-> poll_config_option/poll_config_id @@ -3603,7 +3552,7 @@ SQL nt:1GrR => poll_config_rating_score/option_ids:-> poll_config_option/poll_co FIELD 1rR:1GrR => poll_config_rating_approval/poll_id:-> poll/config_id SQL nt:1GrR => poll_config_rating_approval/option_ids:-> poll_config_option/poll_config_id -FIELD 1GrR:nt,nt,nt => poll_config_option/poll_config_id:-> poll_config_selection/option_ids,poll_config_rating_score/option_ids,poll_config_rating_approval/option_ids +FIELD 1GrR:nt,nt,nt,nt => poll_config_option/poll_config_id:-> poll_config_approval/option_ids,poll_config_selection/option_ids,poll_config_rating_score/option_ids,poll_config_rating_approval/option_ids FIELD 1r:nr => poll_config_option/meeting_user_id:-> meeting_user/poll_option_ids FIELD 1rR:nr => ballot/poll_id:-> poll/ballot_ids @@ -3624,13 +3573,6 @@ FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids -SQL nt:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id -FIELD 1rR:nt => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids - -FIELD 1rR:nt => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids -FIELD 1r:nt => poll_candidate/user_id:-> user/poll_candidate_ids -FIELD 1rR:nt => poll_candidate/meeting_id:-> meeting/poll_candidate_ids - FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id diff --git a/models.yml b/models.yml index aaaa1b96..8fb0134f 100644 --- a/models.yml +++ b/models.yml @@ -427,10 +427,6 @@ user: to: meeting_user/user_id restriction_mode: A on_delete: CASCADE - poll_candidate_ids: - type: relation-list - to: poll_candidate/user_id - restriction_mode: A home_committee_id: type: relation to: committee/native_user_ids @@ -1456,16 +1452,6 @@ meeting: required: true restriction_mode: B - # poll list election - poll_candidate_list_ids: - type: relation-list - to: poll_candidate_list/meeting_id - restriction_mode: B - poll_candidate_ids: - type: relation-list - to: poll_candidate/meeting_id - restriction_mode: B - # Users meeting_user_ids: type: relation-list @@ -3537,6 +3523,11 @@ poll_config_approval: reference: poll required: true restriction_mode: A + option_ids: + type: relation-list + to: poll_config_option/poll_config_id + on_delete: CASCADE + restriction_mode: A allow_abstain: type: boolean default: true @@ -3633,10 +3624,12 @@ poll_config_option: poll_config_id: type: generic-relation to: + - poll_config_approval/option_ids - poll_config_selection/option_ids - poll_config_rating_score/option_ids - poll_config_rating_approval/option_ids reference: + - poll_config_approval - poll_config_selection - poll_config_rating_score - poll_config_rating_approval @@ -3810,51 +3803,6 @@ assignment_candidate: restriction_mode: A constant: true -# models for the poll list election -poll_candidate_list: - id: *id_field - poll_candidate_ids: - type: relation-list - to: poll_candidate/poll_candidate_list_id - on_delete: CASCADE - restriction_mode: A - equal_fields: meeting_id - meeting_id: - type: relation - reference: meeting - to: meeting/poll_candidate_list_ids - required: true - restriction_mode: A - constant: true - -poll_candidate: - id: *id_field - poll_candidate_list_id: - type: relation - reference: poll_candidate_list - to: poll_candidate_list/poll_candidate_ids - equal_fields: meeting_id - restriction_mode: A - required: true - constant: true - user_id: - type: relation - reference: user - to: user/poll_candidate_ids - restriction_mode: A - constant: true - weight: - type: number - required: true - restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/poll_candidate_ids - required: true - restriction_mode: A - constant: true - # Mediafiles are delivered by the mediafile server with the URL # `/media//path` mediafile: From 0d0c670221f1aa28694df50741ab65670bf6d61b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 13 Nov 2025 11:07:46 +0100 Subject: [PATCH 124/142] Remove vote (#343) --- dev/sql/schema_relational.sql | 475 +++++++++++++++++----------------- models.yml | 460 ++++++++++++++++++-------------- 2 files changed, 499 insertions(+), 436 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 73ff4b4f..14e4d283 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'a86f14641a8c926d16b692535739655c' +-- MODELS_YML_CHECKSUM = '22ab0cf0aa5fb1b778851b7f5aada0fe' -- Function and meta table definitions @@ -434,7 +434,7 @@ CREATE TABLE meeting_t ( name varchar(100) NOT NULL DEFAULT 'OpenSlides', is_active_in_organization_id integer, is_archived_in_organization_id integer, - description varchar(100) DEFAULT 'Presentation and assembly system', + description varchar(100), location varchar(256), start_time timestamptz, end_time timestamptz, @@ -538,6 +538,8 @@ CREATE TABLE meeting_t ( motions_export_preamble text, motions_export_submitter_recommendation boolean DEFAULT True, motions_export_follow_recommendation boolean DEFAULT False, + motions_enable_restricted_editor_for_manager boolean, + motions_enable_restricted_editor_for_non_manager boolean, motion_poll_ballot_paper_selection varchar(256) CONSTRAINT enum_meeting_motion_poll_ballot_paper_selection CHECK (motion_poll_ballot_paper_selection IN ('NUMBER_OF_DELEGATES', 'NUMBER_OF_ALL_PARTICIPANTS', 'CUSTOM_NUMBER')) DEFAULT 'CUSTOM_NUMBER', motion_poll_ballot_paper_number integer DEFAULT 8, motion_poll_default_type varchar(256) DEFAULT 'pseudoanonymous', @@ -589,9 +591,8 @@ This email was generated automatically.', poll_default_type varchar(256) DEFAULT 'analog', poll_default_method varchar(256), poll_default_onehundred_percent_base varchar(256) CONSTRAINT enum_meeting_poll_default_onehundred_percent_base CHECK (poll_default_onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'YNA', + poll_default_backend varchar(256) CONSTRAINT enum_meeting_poll_default_backend CHECK (poll_default_backend IN ('long', 'fast')) DEFAULT 'fast', poll_default_live_voting_enabled boolean DEFAULT False, - poll_default_allow_invalid boolean DEFAULT False, - poll_default_allow_vote_split boolean DEFAULT False, poll_couple_countdown boolean DEFAULT True, logo_projector_main_id integer UNIQUE, logo_projector_header_id integer UNIQUE, @@ -625,9 +626,7 @@ comment on column meeting_t.is_active_in_organization_id is 'Backrelation and bo comment on column meeting_t.is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column meeting_t.list_of_speakers_default_structure_level_time is '0 disables structure level countdowns.'; comment on column meeting_t.list_of_speakers_intervention_time is '0 disables intervention speakers.'; -comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.published'' before finished option suggested to user. Is not used in the validations.'; -comment on column meeting_t.poll_default_allow_invalid is 'Defines defaut `poll.allow_invalid` option suggested to user.'; -comment on column meeting_t.poll_default_allow_vote_split is 'Defines defaut `poll.allow_vote_split` option suggested to user.'; +comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.live_voting_enabled'' option suggested to user. Is not used in the validations.'; CREATE TABLE structure_level_t ( @@ -841,7 +840,7 @@ comment on column motion_t.marked_forwarded is 'Forwarded amendments can be mark CREATE TABLE motion_submitter_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, - meeting_user_id integer NOT NULL, + meeting_user_id integer, motion_id integer NOT NULL, meeting_id integer NOT NULL ); @@ -852,7 +851,7 @@ CREATE TABLE motion_submitter_t ( CREATE TABLE motion_editor_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, - meeting_user_id integer NOT NULL, + meeting_user_id integer, motion_id integer NOT NULL, meeting_id integer NOT NULL ); @@ -863,7 +862,7 @@ CREATE TABLE motion_editor_t ( CREATE TABLE motion_working_group_speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, - meeting_user_id integer NOT NULL, + meeting_user_id integer, motion_id integer NOT NULL, meeting_id integer NOT NULL ); @@ -964,6 +963,7 @@ CREATE TABLE motion_state_t ( allow_motion_forwarding boolean DEFAULT False, allow_amendment_forwarding boolean, set_workflow_timestamp boolean DEFAULT False, + state_button_label varchar(256), submitter_withdraw_state_id integer, workflow_id integer NOT NULL, meeting_id integer NOT NULL @@ -989,18 +989,24 @@ comment on column motion_workflow_t.sequential_number is 'The (positive) serial CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256) NOT NULL, - config_id varchar(100) NOT NULL, - config_id_poll_config_approval_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_approval' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - config_id_poll_config_selection_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_selection' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - config_id_poll_config_rating_score_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_rating_score' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - config_id_poll_config_rating_approval_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(config_id, '/', 1) = 'poll_config_rating_approval' THEN cast(split_part(config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_config_id_part1 CHECK (split_part(config_id, '/', 1) IN ('poll_config_approval','poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), - visibility varchar(256) NOT NULL CONSTRAINT enum_poll_visibility CHECK (visibility IN ('manually', 'named', 'open', 'secret')), - state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished')) DEFAULT 'created', - result text, - published boolean DEFAULT False, - allow_invalid boolean DEFAULT False, - allow_vote_split boolean DEFAULT False, + description varchar(256), + type varchar(256) NOT NULL CONSTRAINT enum_poll_type CHECK (type IN ('analog', 'named', 'pseudoanonymous', 'cryptographic')), + backend varchar(256) NOT NULL CONSTRAINT enum_poll_backend CHECK (backend IN ('long', 'fast')) DEFAULT 'fast', + is_pseudoanonymized boolean, + pollmethod varchar(256) NOT NULL CONSTRAINT enum_poll_pollmethod CHECK (pollmethod IN ('Y', 'YN', 'YNA', 'N')), + state varchar(256) CONSTRAINT enum_poll_state CHECK (state IN ('created', 'started', 'finished', 'published')) DEFAULT 'created', + min_votes_amount integer CONSTRAINT minimum_min_votes_amount CHECK (min_votes_amount >= 1) DEFAULT 1, + max_votes_amount integer CONSTRAINT minimum_max_votes_amount CHECK (max_votes_amount >= 1) DEFAULT 1, + max_votes_per_option integer CONSTRAINT minimum_max_votes_per_option CHECK (max_votes_per_option >= 1) DEFAULT 1, + global_yes boolean DEFAULT False, + global_no boolean DEFAULT False, + global_abstain boolean DEFAULT False, + onehundred_percent_base varchar(256) NOT NULL CONSTRAINT enum_poll_onehundred_percent_base CHECK (onehundred_percent_base IN ('Y', 'YN', 'YNA', 'N', 'valid', 'cast', 'entitled', 'entitled_present', 'disabled')) DEFAULT 'disabled', + votesvalid decimal(16,6), + votesinvalid decimal(16,6), + votescast decimal(16,6), + entitled_users_at_stop jsonb, + live_voting_enabled boolean DEFAULT False, sequential_number integer NOT NULL, CONSTRAINT unique_poll_sequential_number UNIQUE (sequential_number, meeting_id), content_object_id varchar(100) NOT NULL, @@ -1008,86 +1014,51 @@ CREATE TABLE poll_t ( content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','assignment','topic')), + global_option_id integer UNIQUE, meeting_id integer NOT NULL ); -comment on column poll_t.result is 'Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually.'; -comment on column poll_t.published is 'If true, users can see the result.'; -comment on column poll_t.allow_invalid is 'If true, the vote service does not validate. This is always the case for secret polls.'; -comment on column poll_t.allow_vote_split is 'If true, users can split there vote.'; +comment on column poll_t.live_voting_enabled is 'If true, the vote service sends the votes of the users to the autoupdate service.'; comment on column poll_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +/* + Fields without SQL definition for table poll -CREATE TABLE poll_config_approval_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_id integer NOT NULL UNIQUE, - allow_abstain boolean DEFAULT True -); - - - - -CREATE TABLE poll_config_selection_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_id integer NOT NULL UNIQUE, - max_options_amount integer DEFAULT 0, - min_options_amount integer DEFAULT 0, - allow_nota boolean DEFAULT False -); - - - - -CREATE TABLE poll_config_rating_score_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_id integer NOT NULL UNIQUE, - max_options_amount integer DEFAULT 0, - min_options_amount integer DEFAULT 0, - max_votes_per_option integer DEFAULT 0, - max_vote_sum integer DEFAULT 0, - min_vote_sum integer DEFAULT 0 -); - - - - -CREATE TABLE poll_config_rating_approval_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_id integer NOT NULL UNIQUE, - max_options_amount integer DEFAULT 0, - min_options_amount integer DEFAULT 0, - allow_abstain boolean DEFAULT True -); - - + poll/live_votes: type:JSON is marked as a calculated field and not generated in schema +*/ -CREATE TABLE poll_config_option_t ( +CREATE TABLE option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_config_id varchar(100) NOT NULL, - poll_config_id_poll_config_approval_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_approval' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - poll_config_id_poll_config_selection_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_selection' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - poll_config_id_poll_config_rating_score_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_score' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - poll_config_id_poll_config_rating_approval_id integer GENERATED ALWAYS AS (CASE WHEN split_part(poll_config_id, '/', 1) = 'poll_config_rating_approval' THEN cast(split_part(poll_config_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_poll_config_id_part1 CHECK (split_part(poll_config_id, '/', 1) IN ('poll_config_approval','poll_config_selection','poll_config_rating_score','poll_config_rating_approval')), - weight integer, - text varchar(256), - meeting_user_id integer + weight integer DEFAULT 10000, + text text, + yes decimal(16,6), + no decimal(16,6), + abstain decimal(16,6), + poll_id integer, + used_as_global_option_in_poll_id integer UNIQUE, + content_object_id varchar(100), + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_candidate_list_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), + meeting_id integer NOT NULL ); -CREATE TABLE ballot_t ( +CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight decimal(16,6) DEFAULT '1.000000', - split boolean DEFAULT False, - value text, - poll_id integer NOT NULL, - acting_meeting_user_id integer, - represented_meeting_user_id integer + weight decimal(16,6), + value varchar(256), + user_token varchar(256) NOT NULL, + option_id integer NOT NULL, + user_id integer, + delegated_user_id integer, + meeting_id integer NOT NULL ); @@ -1122,6 +1093,25 @@ CREATE TABLE assignment_candidate_t ( +CREATE TABLE poll_candidate_list_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + meeting_id integer NOT NULL +); + + + + +CREATE TABLE poll_candidate_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + poll_candidate_list_id integer NOT NULL, + user_id integer, + weight integer NOT NULL, + meeting_id integer NOT NULL +); + + + + CREATE TABLE mediafile_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, title varchar(256), @@ -1211,7 +1201,6 @@ CREATE TABLE projection_t ( stable boolean DEFAULT False, weight integer, type varchar(256), - content jsonb, current_projector_id integer, preview_projector_id integer, history_projector_id integer, @@ -1233,6 +1222,12 @@ CREATE TABLE projection_t ( +/* + Fields without SQL definition for table projection + + projection/content: type:JSON is marked as a calculated field and not generated in schema + +*/ CREATE TABLE projector_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1339,12 +1334,6 @@ CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( PRIMARY KEY (meeting_user_id, motion_id) ); -CREATE TABLE nm_meeting_user_poll_voted_ids_poll_t ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (meeting_user_id, poll_id) -); - CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1468,6 +1457,12 @@ CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( PRIMARY KEY (next_state_id, previous_state_id) ); +CREATE TABLE nm_poll_voted_ids_user_t ( + poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (poll_id, user_id) +); + CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1537,6 +1532,11 @@ CREATE VIEW "user" AS SELECT *, , (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, (select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, (select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, (select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, ( @@ -1560,10 +1560,6 @@ CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, (select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_meeting_user_poll_voted_ids_poll_t n where n.meeting_user_id = m.id) as poll_voted_ids, -(select array_agg(p.id ORDER BY p.id) from poll_config_option_t p where p.meeting_user_id = m.id) as poll_option_ids, -(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.acting_meeting_user_id = m.id) as acting_ballot_ids, -(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.represented_meeting_user_id = m.id) as represented_ballot_ids, (select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, (select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, (select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids @@ -1625,6 +1621,8 @@ comment on column "committee".user_ids is 'Calculated field: All users which are CREATE VIEW "meeting" AS SELECT *, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.meeting_id = m.id) as poll_candidate_ids, (select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.meeting_id = m.id) as meeting_user_ids, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_assignment_poll_default_id = m.id) as assignment_poll_default_group_ids, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_poll_default_id = m.id) as poll_default_group_ids, @@ -1656,6 +1654,8 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.meeting_id = m.id) as motion_state_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.meeting_id = m.id) as poll_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.meeting_id = m.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.meeting_id = m.id) as vote_ids, (select array_agg(a.id ORDER BY a.id) from assignment_t a where a.meeting_id = m.id) as assignment_ids, (select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_id = m.id) as assignment_candidate_ids, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_id = m.id) as personal_note_ids, @@ -1776,6 +1776,7 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_motion_id = m.id) as option_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.motion_id = m.id) as change_recommendation_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.motion_id = m.id) as comment_ids, (select a.id from agenda_item_t a where a.content_object_id_motion_id = m.id) as agenda_item_id, @@ -1842,37 +1843,19 @@ FROM motion_workflow_t m; CREATE VIEW "poll" AS SELECT *, -(select array_agg(b.id ORDER BY b.id) from ballot_t b where b.poll_id = p.id) as ballot_ids, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_poll_voted_ids_poll_t n where n.poll_id = p.id) as voted_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, (select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, (select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids FROM poll_t p; -CREATE VIEW "poll_config_approval" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_approval_id = p.id) as option_ids -FROM poll_config_approval_t p; - - -CREATE VIEW "poll_config_selection" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_selection_id = p.id) as option_ids -FROM poll_config_selection_t p; +CREATE VIEW "option" AS SELECT *, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.option_id = o.id) as vote_ids +FROM option_t o; -CREATE VIEW "poll_config_rating_score" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_rating_score_id = p.id) as option_ids -FROM poll_config_rating_score_t p; - - -CREATE VIEW "poll_config_rating_approval" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_config_option_t pc where pc.poll_config_id_poll_config_rating_approval_id = p.id) as option_ids -FROM poll_config_rating_approval_t p; - - -CREATE VIEW "poll_config_option" AS SELECT * FROM poll_config_option_t p; - - -CREATE VIEW "ballot" AS SELECT * FROM ballot_t b; +CREATE VIEW "vote" AS SELECT * FROM vote_t v; CREATE VIEW "assignment" AS SELECT *, @@ -1890,6 +1873,15 @@ FROM assignment_t a; CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; +CREATE VIEW "poll_candidate_list" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, +(select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id +FROM poll_candidate_list_t p; + + +CREATE VIEW "poll_candidate" AS SELECT * FROM poll_candidate_t p; + + CREATE VIEW "mediafile" AS SELECT *, (select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, (select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids @@ -2097,32 +2089,23 @@ ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_approval_id) REFERENCES poll_config_approval_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_selection_id) REFERENCES poll_config_selection_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_rating_score_id) REFERENCES poll_config_rating_score_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(config_id_poll_config_rating_approval_id) REFERENCES poll_config_rating_approval_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_approval_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; - -ALTER TABLE poll_config_selection_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; - -ALTER TABLE poll_config_rating_score_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; - -ALTER TABLE poll_config_rating_approval_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(used_as_global_option_in_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_approval_id) REFERENCES poll_config_approval_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_selection_id) REFERENCES poll_config_selection_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_score_id) REFERENCES poll_config_rating_score_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_option_t ADD FOREIGN KEY(poll_config_id_poll_config_rating_approval_id) REFERENCES poll_config_rating_approval_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_config_option_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; - -ALTER TABLE ballot_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; -ALTER TABLE ballot_t ADD FOREIGN KEY(acting_meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE ballot_t ADD FOREIGN KEY(represented_meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2130,6 +2113,12 @@ ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES ass ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2284,6 +2273,14 @@ CREATE CONSTRAINT TRIGGER tr_ud_assignment_list_of_speakers_id AFTER UPDATE OF c FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'content_object_id_assignment_id'); +-- definition trigger not null for poll_candidate_list.option_id against option.content_object_id_poll_candidate_list_id +CREATE CONSTRAINT TRIGGER tr_i_poll_candidate_list_option_id AFTER INSERT ON poll_candidate_list_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_poll_candidate_list_option_id AFTER UPDATE OF content_object_id_poll_candidate_list_id OR DELETE ON option_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', 'content_object_id_poll_candidate_list_id'); + + -- Create triggers checking foreign_id not null for relation-lists @@ -2446,11 +2443,6 @@ DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_e CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); -CREATE TRIGGER tr_log_nm_meeting_user_poll_voted_ids_poll_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_poll_voted_ids_poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','poll','poll_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_poll_voted_ids_poll_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - CREATE TRIGGER tr_log_nm_meeting_user_structure_level_ids_structure_level_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','structure_level','structure_level_id'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t @@ -2908,18 +2900,6 @@ CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELET DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_poll_config_approval_config_id_poll_config_approval_id AFTER INSERT OR UPDATE OF config_id_poll_config_approval_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_approval','config_id_poll_config_approval_id'); - -CREATE TRIGGER tr_log_poll_config_selection_config_id_poll_config_selection_id AFTER INSERT OR UPDATE OF config_id_poll_config_selection_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_selection','config_id_poll_config_selection_id'); - -CREATE TRIGGER tr_log_poll_config_rating_score_config_id_poll_config_rating_score_id AFTER INSERT OR UPDATE OF config_id_poll_config_rating_score_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_score','config_id_poll_config_rating_score_id'); - -CREATE TRIGGER tr_log_poll_config_rating_approval_config_id_poll_config_rating_approval_id AFTER INSERT OR UPDATE OF config_id_poll_config_rating_approval_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_approval','config_id_poll_config_rating_approval_id'); - CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); @@ -2928,72 +2908,50 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_ CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); -CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_poll_config_approval AFTER INSERT OR UPDATE OR DELETE ON poll_config_approval_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_approval'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_approval_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_poll_config_approval_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_approval_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); - -CREATE TRIGGER tr_log_poll_config_selection AFTER INSERT OR UPDATE OR DELETE ON poll_config_selection_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_selection'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_selection_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_t_global_option_id AFTER INSERT OR UPDATE OF global_option_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'global_option_id'); -CREATE TRIGGER tr_log_poll_config_selection_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_selection_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); - -CREATE TRIGGER tr_log_poll_config_rating_score AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_score_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_rating_score'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_score_t +CREATE TRIGGER tr_log_nm_poll_voted_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','poll_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_poll_config_rating_score_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_rating_score_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); - -CREATE TRIGGER tr_log_poll_config_rating_approval AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_approval_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_rating_approval'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_rating_approval_t +CREATE TRIGGER tr_log_option AFTER INSERT OR UPDATE OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('option'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON option_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_poll_config_rating_approval_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON poll_config_rating_approval_t +CREATE TRIGGER tr_log_option_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON option_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); +CREATE TRIGGER tr_log_option_t_used_as_global_option_in_poll_id AFTER INSERT OR UPDATE OF used_as_global_option_in_poll_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'used_as_global_option_in_poll_id'); -CREATE TRIGGER tr_log_poll_config_option AFTER INSERT OR UPDATE OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_config_option'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_config_option_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - - -CREATE TRIGGER tr_log_poll_config_approval_poll_config_id_poll_config_approval_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_approval_id OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_approval','poll_config_id_poll_config_approval_id'); - -CREATE TRIGGER tr_log_poll_config_selection_poll_config_id_poll_config_selection_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_selection_id OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_selection','poll_config_id_poll_config_selection_id'); +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); -CREATE TRIGGER tr_log_poll_config_rating_score_poll_config_id_poll_config_rating_score_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_rating_score_id OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_score','poll_config_id_poll_config_rating_score_id'); +CREATE TRIGGER tr_log_user_content_object_id_user_id AFTER INSERT OR UPDATE OF content_object_id_user_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user','content_object_id_user_id'); -CREATE TRIGGER tr_log_poll_config_rating_approval_poll_config_id_poll_config_rating_approval_id AFTER INSERT OR UPDATE OF poll_config_id_poll_config_rating_approval_id OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_config_rating_approval','poll_config_id_poll_config_rating_approval_id'); -CREATE TRIGGER tr_log_poll_config_option_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON poll_config_option_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_poll_candidate_list_content_object_id_poll_candidate_list_id AFTER INSERT OR UPDATE OF content_object_id_poll_candidate_list_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list','content_object_id_poll_candidate_list_id'); +CREATE TRIGGER tr_log_option_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON option_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_ballot AFTER INSERT OR UPDATE OR DELETE ON ballot_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('ballot'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON ballot_t +CREATE TRIGGER tr_log_vote AFTER INSERT OR UPDATE OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_ballot_t_poll_id AFTER INSERT OR UPDATE OF poll_id OR DELETE ON ballot_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll', 'poll_id'); -CREATE TRIGGER tr_log_ballot_t_acting_meeting_user_id AFTER INSERT OR UPDATE OF acting_meeting_user_id OR DELETE ON ballot_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'acting_meeting_user_id'); -CREATE TRIGGER tr_log_ballot_t_represented_meeting_user_id AFTER INSERT OR UPDATE OF represented_meeting_user_id OR DELETE ON ballot_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'represented_meeting_user_id'); +CREATE TRIGGER tr_log_vote_t_option_id AFTER INSERT OR UPDATE OF option_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'option_id'); +CREATE TRIGGER tr_log_vote_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_vote_t_delegated_user_id AFTER INSERT OR UPDATE OF delegated_user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'delegated_user_id'); +CREATE TRIGGER tr_log_vote_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); @@ -3015,6 +2973,26 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeti CREATE TRIGGER tr_log_assignment_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_poll_candidate_list AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_poll_candidate_list_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_poll_candidate AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_poll_candidate_t_poll_candidate_list_id AFTER INSERT OR UPDATE OF poll_candidate_list_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list', 'poll_candidate_list_id'); +CREATE TRIGGER tr_log_poll_candidate_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_poll_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + CREATE TRIGGER tr_log_mediafile AFTER INSERT OR UPDATE OR DELETE ON mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t @@ -3255,6 +3233,11 @@ SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids SQL nts:nts => user/committee_ids:-> committee/user_ids SQL nt:nt => user/committee_management_ids:-> committee/manager_ids SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id +SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids +SQL nr:1Gr => user/option_ids:-> option/content_object_id +SQL nr:1r => user/vote_ids:-> vote/user_id +SQL nr:1r => user/delegated_vote_ids:-> vote/delegated_user_id +SQL nr:1r => user/poll_candidate_ids:-> poll_candidate/user_id FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids SQL nt:1r => user/history_position_ids:-> history_position/user_id SQL nt:1Gr => user/history_entry_ids:-> history_entry/model_id @@ -3265,16 +3248,12 @@ FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids -SQL nt:1rR => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id -SQL nt:1rR => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id -SQL nt:1rR => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id +SQL nt:1r => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id +SQL nt:1r => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id +SQL nt:1r => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id -SQL nt:nt => meeting_user/poll_voted_ids:-> poll/voted_ids -SQL nr:1r => meeting_user/poll_option_ids:-> poll_config_option/meeting_user_id -SQL nt:1r => meeting_user/acting_ballot_ids:-> ballot/acting_meeting_user_id -SQL nt:1r => meeting_user/represented_ballot_ids:-> ballot/represented_meeting_user_id SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids @@ -3304,6 +3283,8 @@ FIELD 1r:nt => meeting/template_for_organization_id:-> organization/template_mee FIELD 1rR:1t => meeting/motions_default_workflow_id:-> motion_workflow/default_workflow_meeting_id FIELD 1rR:1t => meeting/motions_default_amendment_workflow_id:-> motion_workflow/default_amendment_workflow_meeting_id SQL nt:1r => meeting/motion_poll_default_group_ids:-> group/used_as_motion_poll_default_id +SQL nr:1rR => meeting/poll_candidate_list_ids:-> poll_candidate_list/meeting_id +SQL nr:1rR => meeting/poll_candidate_ids:-> poll_candidate/meeting_id SQL nt:1rR => meeting/meeting_user_ids:-> meeting_user/meeting_id SQL nt:1r => meeting/assignment_poll_default_group_ids:-> group/used_as_assignment_poll_default_id SQL nt:1r => meeting/poll_default_group_ids:-> group/used_as_poll_default_id @@ -3334,7 +3315,9 @@ SQL nt:1rR => meeting/motion_editor_ids:-> motion_editor/meeting_id SQL nt:1rR => meeting/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_id SQL nt:1rR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id SQL nt:1rR => meeting/motion_state_ids:-> motion_state/meeting_id -SQL nt:1rR => meeting/poll_ids:-> poll/meeting_id +SQL nr:1rR => meeting/poll_ids:-> poll/meeting_id +SQL nr:1rR => meeting/option_ids:-> option/meeting_id +SQL nr:1rR => meeting/vote_ids:-> vote/meeting_id SQL nt:1rR => meeting/assignment_ids:-> assignment/meeting_id SQL nt:1rR => meeting/assignment_candidate_ids:-> assignment_candidate/meeting_id SQL nt:1rR => meeting/personal_note_ids:-> personal_note/meeting_id @@ -3470,6 +3453,7 @@ SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ SQL nt:1rR => motion/editor_ids:-> motion_editor/motion_id SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id +SQL nr:1Gr => motion/option_ids:-> option/content_object_id SQL nt:1rR => motion/change_recommendation_ids:-> motion_change_recommendation/motion_id SQL nt:1rR => motion/comment_ids:-> motion_comment/motion_id SQL 1t:1GrR => motion/agenda_item_id:-> agenda_item/content_object_id @@ -3481,15 +3465,15 @@ SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id FIELD 1rR:nt => motion/meeting_id:-> meeting/motion_ids SQL nt:1Gr => motion/history_entry_ids:-> history_entry/model_id -FIELD 1rR:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids +FIELD 1r:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids FIELD 1rR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids -FIELD 1rR:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids +FIELD 1r:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids FIELD 1rR:nt => motion_editor/motion_id:-> motion/editor_ids FIELD 1rR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids -FIELD 1rR:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids +FIELD 1r:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids FIELD 1rR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids FIELD 1rR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids @@ -3532,32 +3516,24 @@ SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_def SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids -FIELD 1GrR:1rR,1rR,1rR,1rR => poll/config_id:-> poll_config_approval/poll_id,poll_config_selection/poll_id,poll_config_rating_score/poll_id,poll_config_rating_approval/poll_id FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids -SQL nr:1rR => poll/ballot_ids:-> ballot/poll_id -SQL nt:nt => poll/voted_ids:-> meeting_user/poll_voted_ids +SQL nr:1r => poll/option_ids:-> option/poll_id +FIELD 1r:1r => poll/global_option_id:-> option/used_as_global_option_in_poll_id +SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => poll/meeting_id:-> meeting/poll_ids +FIELD 1rR:nr => poll/meeting_id:-> meeting/poll_ids -FIELD 1rR:1GrR => poll_config_approval/poll_id:-> poll/config_id -SQL nt:1GrR => poll_config_approval/option_ids:-> poll_config_option/poll_config_id +FIELD 1r:nr => option/poll_id:-> poll/option_ids +FIELD 1r:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id +SQL nr:1rR => option/vote_ids:-> vote/option_id +FIELD 1Gr:nr,nr,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id +FIELD 1rR:nr => option/meeting_id:-> meeting/option_ids -FIELD 1rR:1GrR => poll_config_selection/poll_id:-> poll/config_id -SQL nt:1GrR => poll_config_selection/option_ids:-> poll_config_option/poll_config_id - -FIELD 1rR:1GrR => poll_config_rating_score/poll_id:-> poll/config_id -SQL nt:1GrR => poll_config_rating_score/option_ids:-> poll_config_option/poll_config_id - -FIELD 1rR:1GrR => poll_config_rating_approval/poll_id:-> poll/config_id -SQL nt:1GrR => poll_config_rating_approval/option_ids:-> poll_config_option/poll_config_id - -FIELD 1GrR:nt,nt,nt,nt => poll_config_option/poll_config_id:-> poll_config_approval/option_ids,poll_config_selection/option_ids,poll_config_rating_score/option_ids,poll_config_rating_approval/option_ids -FIELD 1r:nr => poll_config_option/meeting_user_id:-> meeting_user/poll_option_ids - -FIELD 1rR:nr => ballot/poll_id:-> poll/ballot_ids -FIELD 1r:nt => ballot/acting_meeting_user_id:-> meeting_user/acting_ballot_ids -FIELD 1r:nt => ballot/represented_meeting_user_id:-> meeting_user/represented_ballot_ids +FIELD 1rR:nr => vote/option_id:-> option/vote_ids +FIELD 1r:nr => vote/user_id:-> user/vote_ids +FIELD 1r:nr => vote/delegated_user_id:-> user/delegated_vote_ids +FIELD 1rR:nr => vote/meeting_id:-> meeting/vote_ids SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id @@ -3573,6 +3549,14 @@ FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids +SQL nr:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id +FIELD 1rR:nr => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids +SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id + +FIELD 1rR:nr => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids +FIELD 1r:nr => poll_candidate/user_id:-> user/poll_candidate_ids +FIELD 1rR:nr => poll_candidate/meeting_id:-> meeting/poll_candidate_ids + FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id @@ -3654,5 +3638,10 @@ FIELD 1rR:nt => history_entry/position_id:-> history_position/entry_ids FIELD 1r:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids */ +/* +There are 2 errors/warnings + poll/live_votes: type:JSON is marked as a calculated field and not generated in schema + projection/content: type:JSON is marked as a calculated field and not generated in schema +*/ /* Missing attribute handling for constant, on_delete, equal_fields, deferred */ \ No newline at end of file diff --git a/models.yml b/models.yml index b8db276c..f789d5ef 100644 --- a/models.yml +++ b/models.yml @@ -427,6 +427,30 @@ user: to: meeting_user/user_id restriction_mode: A on_delete: CASCADE + poll_voted_ids: + type: relation-list + to: poll/voted_ids + restriction_mode: A + option_ids: + type: relation-list + to: option/content_object_id + reference: option + restriction_mode: A + vote_ids: + type: relation-list + to: vote/user_id + reference: vote + restriction_mode: A + delegated_vote_ids: + type: relation-list + to: vote/delegated_user_id + reference: vote + restriction_mode: A + poll_candidate_ids: + type: relation-list + to: poll_candidate/user_id + reference: poll_candidate + restriction_mode: A home_committee_id: type: relation to: committee/native_user_ids @@ -545,24 +569,6 @@ meeting_user: to: meeting_user/vote_delegated_to_id equal_fields: meeting_id restriction_mode: A - poll_voted_ids: - type: relation-list - to: poll/voted_ids - restriction_mode: A - poll_option_ids: - type: relation-list - to: poll_config_option/meeting_user_id - reference: poll_config_option - required: false - restriction_mode: A - acting_ballot_ids: - type: relation-list - to: ballot/acting_meeting_user_id - restriction_mode: A - represented_ballot_ids: - type: relation-list - to: ballot/represented_meeting_user_id - restriction_mode: A chat_message_ids: type: relation-list to: chat_message/meeting_user_id @@ -1454,6 +1460,18 @@ meeting: required: true restriction_mode: B + # poll list election + poll_candidate_list_ids: + type: relation-list + to: poll_candidate_list/meeting_id + reference: poll_candidate_list + restriction_mode: B + poll_candidate_ids: + type: relation-list + to: poll_candidate/meeting_id + reference: poll_candidate + restriction_mode: B + # Users meeting_user_ids: type: relation-list @@ -1622,20 +1640,15 @@ meeting: type: relation-list to: group/used_as_poll_default_id restriction_mode: B - poll_default_live_voting_enabled: - type: boolean - default: false - description: Defines default 'poll.published' before finished option suggested to user. Is not used in the validations. - restriction_mode: B - poll_default_allow_invalid: - type: boolean - default: false - description: Defines defaut `poll.allow_invalid` option suggested to user. + poll_default_backend: + type: string + enum: *poll_backends + default: fast restriction_mode: B - poll_default_allow_vote_split: + poll_default_live_voting_enabled: type: boolean default: false - description: Defines defaut `poll.allow_vote_split` option suggested to user. + description: Defines default 'poll.live_voting_enabled' option suggested to user. Is not used in the validations. restriction_mode: B poll_couple_countdown: type: boolean @@ -1782,6 +1795,19 @@ meeting: poll_ids: type: relation-list to: poll/meeting_id + reference: poll + on_delete: CASCADE + restriction_mode: B + option_ids: + type: relation-list + to: option/meeting_id + reference: option + on_delete: CASCADE + restriction_mode: B + vote_ids: + type: relation-list + to: vote/meeting_id + reference: vote on_delete: CASCADE restriction_mode: B assignment_ids: @@ -2847,6 +2873,13 @@ motion: on_delete: CASCADE equal_fields: meeting_id restriction_mode: C + option_ids: + type: relation-list + to: option/content_object_id + reference: option + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: C change_recommendation_ids: type: relation-list to: motion_change_recommendation/motion_id @@ -3413,28 +3446,35 @@ poll: type: string required: true restriction_mode: A - config_id: - type: generic-relation - to: - - poll_config_approval/poll_id - - poll_config_selection/poll_id - - poll_config_rating_score/poll_id - - poll_config_rating_approval/poll_id - reference: - - poll_config_approval - - poll_config_selection - - poll_config_rating_score - - poll_config_rating_approval - required: true + description: + type: string restriction_mode: A - visibility: + type: type: string required: true enum: - - manually + - analog - named - - open - - secret + - pseudoanonymous + - cryptographic + restriction_mode: A + backend: + type: string + required: True + enum: *poll_backends + default: fast + restriction_mode: A + is_pseudoanonymized: + type: boolean + restriction_mode: A + pollmethod: + type: string + required: true + enum: + - "Y" + - "YN" + - "YNA" + - "N" restriction_mode: A state: type: string @@ -3442,27 +3482,64 @@ poll: - created - started - finished + - published default: created restriction_mode: A - result: - type: text - description: Calculated result. The format depends on the value in poll/method. Can be manually set when visibility is set to manually. - restriction_mode: B - published: + min_votes_amount: + type: number + default: 1 + minimum: 1 + restriction_mode: A + max_votes_amount: + type: number + default: 1 + minimum: 1 + restriction_mode: A + max_votes_per_option: + type: number + default: 1 + minimum: 1 + restriction_mode: A + global_yes: type: boolean default: false - description: If true, users can see the result. restriction_mode: A - allow_invalid: + global_no: type: boolean default: false - description: If true, the vote service does not validate. This is always the case for secret polls. restriction_mode: A - allow_vote_split: + global_abstain: + type: boolean + default: false + restriction_mode: A + onehundred_percent_base: + type: string + required: true + enum: *onehundred_percent_bases + default: disabled + restriction_mode: A + votesvalid: + type: decimal(6) + restriction_mode: B + votesinvalid: + type: decimal(6) + restriction_mode: B + votescast: + type: decimal(6) + restriction_mode: D + entitled_users_at_stop: + type: JSON + restriction_mode: A + live_voting_enabled: type: boolean default: false - description: If true, users can split there vote. + description: If true, the vote service sends the votes of the users to the autoupdate service. restriction_mode: A + live_votes: + type: JSON + calculated: true + description: dict from user to their vote. The value is null, when live voting is disabled. + restriction_mode: C sequential_number: type: number sequence_scope: meeting_id @@ -3487,17 +3564,25 @@ poll: restriction_mode: A required: true constant: true - ballot_ids: + option_ids: type: relation-list - to: ballot/poll_id - reference: ballot + to: option/poll_id + reference: option + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + global_option_id: + type: relation + to: option/used_as_global_option_in_poll_id + reference: option on_delete: CASCADE equal_fields: meeting_id restriction_mode: A + constant: true voted_ids: type: relation-list - to: meeting_user/poll_voted_ids - restriction_mode: B + to: user/poll_voted_ids + restriction_mode: A entitled_group_ids: type: relation-list to: group/poll_ids @@ -3511,178 +3596,114 @@ poll: restriction_mode: A meeting_id: type: relation - reference: meeting to: meeting/poll_ids + reference: meeting restriction_mode: A required: true constant: true -poll_config_approval: +option: id: *id_field - poll_id: - type: relation - to: poll/config_id - reference: poll - required: true - restriction_mode: A - option_ids: - type: relation-list - to: poll_config_option/poll_config_id - on_delete: CASCADE - restriction_mode: A - allow_abstain: - type: boolean - default: true - restriction_mode: A - -poll_config_selection: - id: *id_field - poll_id: - type: relation - to: poll/config_id - reference: poll - required: true - restriction_mode: A - option_ids: - type: relation-list - to: poll_config_option/poll_config_id - on_delete: CASCADE - restriction_mode: A - max_options_amount: - type: number - default: 0 - restriction_mode: A - min_options_amount: + weight: type: number - default: 0 + default: 10000 restriction_mode: A - allow_nota: - type: boolean - default: false + text: + type: HTMLStrict restriction_mode: A + "yes": + type: decimal(6) + restriction_mode: B + "no": + type: decimal(6) + restriction_mode: B + abstain: + type: decimal(6) + restriction_mode: B -poll_config_rating_score: - id: *id_field poll_id: type: relation - to: poll/config_id + to: poll/option_ids reference: poll - required: true - restriction_mode: A - option_ids: - type: relation-list - to: poll_config_option/poll_config_id - on_delete: CASCADE - restriction_mode: A - max_options_amount: - type: number - default: 0 - restriction_mode: A - min_options_amount: - type: number - default: 0 - restriction_mode: A - max_votes_per_option: - type: number - default: 0 - restriction_mode: A - max_vote_sum: - type: number - default: 0 - restriction_mode: A - min_vote_sum: - type: number - default: 0 + equal_fields: meeting_id restriction_mode: A - -poll_config_rating_approval: - id: *id_field - poll_id: + constant: true + used_as_global_option_in_poll_id: type: relation - to: poll/config_id + to: poll/global_option_id reference: poll - required: true + equal_fields: meeting_id restriction_mode: A - option_ids: + constant: true + vote_ids: type: relation-list - to: poll_config_option/poll_config_id + to: vote/option_id + reference: vote on_delete: CASCADE + equal_fields: meeting_id restriction_mode: A - max_options_amount: - type: number - default: 0 - restriction_mode: A - min_options_amount: - type: number - default: 0 - restriction_mode: A - allow_abstain: - type: boolean - default: true - restriction_mode: A - -poll_config_option: - id: *id_field - poll_config_id: + content_object_id: type: generic-relation to: - - poll_config_approval/option_ids - - poll_config_selection/option_ids - - poll_config_rating_score/option_ids - - poll_config_rating_approval/option_ids + - motion/option_ids + - user/option_ids + - poll_candidate_list/option_id reference: - - poll_config_approval - - poll_config_selection - - poll_config_rating_score - - poll_config_rating_approval - required: true - restriction_mode: A - weight: - type: number - restriction_mode: A - text: - type: string + - motion + - user + - poll_candidate_list + equal_fields: meeting_id restriction_mode: A - meeting_user_id: + constant: true + meeting_id: type: relation - to: meeting_user/poll_option_ids - reference: meeting_user - required: false + to: meeting/option_ids + reference: meeting + required: true restriction_mode: A + constant: true -ballot: +vote: id: *id_field weight: type: decimal(6) - restriction_mode: B + restriction_mode: A constant: true - default: "1.000000" - split: - type: boolean - default: false - restriction_mode: B value: - type: text + type: string + restriction_mode: A + constant: true + user_token: + type: string + required: true restriction_mode: B constant: true - poll_id: + + option_id: type: relation - reference: poll - to: poll/ballot_ids + to: option/vote_ids + reference: option equal_fields: meeting_id required: true restriction_mode: A constant: true - acting_meeting_user_id: + user_id: type: relation - reference: meeting_user - to: meeting_user/acting_ballot_ids - restriction_mode: C - represented_meeting_user_id: + to: user/vote_ids + reference: user + restriction_mode: A + delegated_user_id: type: relation - reference: meeting_user - to: meeting_user/represented_ballot_ids - restriction_mode: C + to: user/delegated_vote_ids + reference: user + restriction_mode: A + meeting_id: + type: relation + to: meeting/vote_ids + reference: meeting + required: true + restriction_mode: A + constant: true assignment: id: *id_field @@ -3805,6 +3826,59 @@ assignment_candidate: restriction_mode: A constant: true +# models for the poll list election +poll_candidate_list: + id: *id_field + poll_candidate_ids: + type: relation-list + to: poll_candidate/poll_candidate_list_id + reference: poll_candidate + on_delete: CASCADE + restriction_mode: A + equal_fields: meeting_id + meeting_id: + type: relation + to: meeting/poll_candidate_list_ids + reference: meeting + required: true + restriction_mode: A + constant: true + option_id: + type: relation + to: option/content_object_id + equal_fields: meeting_id + required: true + restriction_mode: A + constant: true + +poll_candidate: + id: *id_field + poll_candidate_list_id: + type: relation + to: poll_candidate_list/poll_candidate_ids + reference: poll_candidate_list + equal_fields: meeting_id + restriction_mode: A + required: true + constant: true + user_id: + type: relation + to: user/poll_candidate_ids + reference: user + restriction_mode: A + constant: true + weight: + type: number + required: true + restriction_mode: A + meeting_id: + type: relation + to: meeting/poll_candidate_ids + reference: meeting + required: true + restriction_mode: A + constant: true + # Mediafiles are delivered by the mediafile server with the URL # `/media//path` mediafile: @@ -4202,7 +4276,7 @@ projection: content: type: JSON - # calculated: true + calculated: true restriction_mode: A current_projector_id: From 4a8a43aa1383fe0d3ec824459a41d175032b2cf8 Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:34:40 +0100 Subject: [PATCH 125/142] [rel-DB] Merge main (#355) --- .github/workflows/continuous_integration.yml | 2 +- .github/workflows/create-pr.yml | 4 +- .github/workflows/pick-to-staging.yml | 2 +- .github/workflows/staging-to-main.yml | 2 +- dev/requirements.txt | 6 +-- dev/sql/schema_relational.sql | 57 ++++++++++++++------ models.yml | 40 ++++++++++++-- 7 files changed, 85 insertions(+), 28 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 28d3befb..37b5e71d 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -19,7 +19,7 @@ jobs: working-directory: dev/ steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index e1766b1e..89a752b8 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -24,12 +24,12 @@ jobs: name: Create or update PR runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: path: current-meta-repository ref: ${{ github.ref_name }} - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: repository: ${{ github.repository_owner }}/${{ inputs.repository }} path: service-repository diff --git a/.github/workflows/pick-to-staging.yml b/.github/workflows/pick-to-staging.yml index c6c0d245..0846557d 100644 --- a/.github/workflows/pick-to-staging.yml +++ b/.github/workflows/pick-to-staging.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout main - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: main fetch-depth: 2 diff --git a/.github/workflows/staging-to-main.yml b/.github/workflows/staging-to-main.yml index 176d0059..67eb46e2 100644 --- a/.github/workflows/staging-to-main.yml +++ b/.github/workflows/staging-to-main.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout main - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: main diff --git a/dev/requirements.txt b/dev/requirements.txt index 523776ce..1131fdfc 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -1,11 +1,11 @@ autoflake==2.3.1 -black==25.9.0 +black==25.11.0 flake8==7.3.0 isort==7.0.0 mypy==1.18.2 sqlfluff==3.4.2 -pytest==8.4.2 -pyupgrade==3.21.0 +pytest==9.0.1 +pyupgrade==3.21.2 pyyaml==6.0.3 simplejson==3.20.2 debugpy==1.8.0 diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 14e4d283..1492966d 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '22ab0cf0aa5fb1b778851b7f5aada0fe' +-- MODELS_YML_CHECKSUM = '40ed6ee4a4afca95d6fbbcc0addd2d93' -- Function and meta table definitions @@ -252,6 +252,7 @@ CREATE TABLE organization_t ( privacy_policy text, login_text text, reset_password_verbose_errors boolean, + disable_forward_with_attachments boolean, enable_electronic_voting boolean, enable_chat boolean, limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, @@ -848,6 +849,16 @@ CREATE TABLE motion_submitter_t ( +CREATE TABLE motion_supporter_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + meeting_user_id integer, + motion_id integer NOT NULL, + meeting_id integer NOT NULL +); + + + + CREATE TABLE motion_editor_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, weight integer, @@ -1328,12 +1339,6 @@ CREATE TABLE history_entry_t ( -- Intermediate table definitions -CREATE TABLE nm_meeting_user_supported_motion_ids_motion_t ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (meeting_user_id, motion_id) -); - CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1554,7 +1559,7 @@ comment on column "user".meeting_ids is 'Calculated. All ids from meetings calcu CREATE VIEW "meeting_user" AS SELECT *, (select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, (select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(n.motion_id ORDER BY n.motion_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.meeting_user_id = m.id) as supported_motion_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_supporter_t ms where ms.meeting_user_id = m.id) as motion_supporter_ids, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, @@ -1649,6 +1654,7 @@ CREATE VIEW "meeting" AS SELECT *, (select array_agg(mw.id ORDER BY mw.id) from motion_workflow_t mw where mw.meeting_id = m.id) as motion_workflow_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_comment_t mc where mc.meeting_id = m.id) as motion_comment_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_id = m.id) as motion_submitter_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_supporter_t ms where ms.meeting_id = m.id) as motion_supporter_ids, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_id = m.id) as motion_editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_id = m.id) as motion_working_group_speaker_ids, (select array_agg(mc.id ORDER BY mc.id) from motion_change_recommendation_t mc where mc.meeting_id = m.id) as motion_change_recommendation_ids, @@ -1772,7 +1778,7 @@ CREATE VIEW "motion" AS SELECT *, (select array_agg(g.recommendation_extension_reference_id ORDER BY g.recommendation_extension_reference_id) from gm_motion_recommendation_extension_reference_ids_t g where g.motion_id = m.id) as recommendation_extension_reference_ids, (select array_agg(g.motion_id ORDER BY g.motion_id) from gm_motion_recommendation_extension_reference_ids_t g where g.recommendation_extension_reference_id_motion_id = m.id) as referenced_in_motion_recommendation_extension_ids, (select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.motion_id = m.id) as submitter_ids, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_supported_motion_ids_motion_t n where n.motion_id = m.id) as supporter_meeting_user_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_supporter_t ms where ms.motion_id = m.id) as supporter_ids, (select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.motion_id = m.id) as editor_ids, (select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.motion_id = m.id) as working_group_speaker_ids, (select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_motion_id = m.id) as poll_ids, @@ -1792,6 +1798,9 @@ FROM motion_t m; CREATE VIEW "motion_submitter" AS SELECT * FROM motion_submitter_t m; +CREATE VIEW "motion_supporter" AS SELECT * FROM motion_supporter_t m; + + CREATE VIEW "motion_editor" AS SELECT * FROM motion_editor_t m; @@ -2060,6 +2069,10 @@ ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeti ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2435,11 +2448,6 @@ CREATE TRIGGER tr_log_meeting_user_t_user_id AFTER INSERT OR UPDATE OF user_id O FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); CREATE TRIGGER tr_log_meeting_user_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_nm_meeting_user_supported_motion_ids_motion_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_supported_motion_ids_motion_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','motion','motion_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_supported_motion_ids_motion_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); @@ -2795,6 +2803,18 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id') CREATE TRIGGER tr_log_motion_submitter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_submitter_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_motion_supporter AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_supporter'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_motion_supporter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_supporter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_supporter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + CREATE TRIGGER tr_log_motion_editor AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t @@ -3247,7 +3267,7 @@ FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id -SQL nt:nt => meeting_user/supported_motion_ids:-> motion/supporter_meeting_user_ids +SQL nt:1r => meeting_user/motion_supporter_ids:-> motion_supporter/meeting_user_id SQL nt:1r => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id SQL nt:1r => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id SQL nt:1r => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id @@ -3311,6 +3331,7 @@ SQL nt:1rR => meeting/motion_block_ids:-> motion_block/meeting_id SQL nt:1rR => meeting/motion_workflow_ids:-> motion_workflow/meeting_id SQL nt:1rR => meeting/motion_comment_ids:-> motion_comment/meeting_id SQL nt:1rR => meeting/motion_submitter_ids:-> motion_submitter/meeting_id +SQL nt:1rR => meeting/motion_supporter_ids:-> motion_supporter/meeting_id SQL nt:1rR => meeting/motion_editor_ids:-> motion_editor/meeting_id SQL nt:1rR => meeting/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_id SQL nt:1rR => meeting/motion_change_recommendation_ids:-> motion_change_recommendation/meeting_id @@ -3449,7 +3470,7 @@ SQL nt:nGt => motion/referenced_in_motion_recommendation_extension_ids:-> motion FIELD 1r:nt => motion/category_id:-> motion_category/motion_ids FIELD 1r:nt => motion/block_id:-> motion_block/motion_ids SQL nt:1rR => motion/submitter_ids:-> motion_submitter/motion_id -SQL nt:nt => motion/supporter_meeting_user_ids:-> meeting_user/supported_motion_ids +SQL nt:1rR => motion/supporter_ids:-> motion_supporter/motion_id SQL nt:1rR => motion/editor_ids:-> motion_editor/motion_id SQL nt:1rR => motion/working_group_speaker_ids:-> motion_working_group_speaker/motion_id SQL nt:1GrR => motion/poll_ids:-> poll/content_object_id @@ -3469,6 +3490,10 @@ FIELD 1r:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids FIELD 1rR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids +FIELD 1r:nt => motion_supporter/meeting_user_id:-> meeting_user/motion_supporter_ids +FIELD 1rR:nt => motion_supporter/motion_id:-> motion/supporter_ids +FIELD 1rR:nt => motion_supporter/meeting_id:-> meeting/motion_supporter_ids + FIELD 1r:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids FIELD 1rR:nt => motion_editor/motion_id:-> motion/editor_ids FIELD 1rR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids diff --git a/models.yml b/models.yml index f789d5ef..daa2c385 100644 --- a/models.yml +++ b/models.yml @@ -160,6 +160,9 @@ organization: to: gender/organization_id reference: gender restriction_mode: A + disable_forward_with_attachments: + type: boolean + restriction_mode: A # Configuration (only for the server owner) enable_electronic_voting: @@ -533,9 +536,9 @@ meeting_user: to: speaker/meeting_user_id equal_fields: meeting_id restriction_mode: A - supported_motion_ids: + motion_supporter_ids: type: relation-list - to: motion/supporter_meeting_user_ids + to: motion_supporter/meeting_user_id equal_fields: meeting_id restriction_mode: A motion_editor_ids: @@ -1772,6 +1775,11 @@ meeting: to: motion_submitter/meeting_id on_delete: CASCADE restriction_mode: B + motion_supporter_ids: + type: relation-list + to: motion_supporter/meeting_id + on_delete: CASCADE + restriction_mode: B motion_editor_ids: type: relation-list to: motion_editor/meeting_id @@ -2850,9 +2858,10 @@ motion: on_delete: CASCADE equal_fields: meeting_id restriction_mode: C - supporter_meeting_user_ids: + supporter_ids: type: relation-list - to: meeting_user/supported_motion_ids + to: motion_supporter/motion_id + on_delete: CASCADE equal_fields: meeting_id restriction_mode: C editor_ids: @@ -2967,6 +2976,29 @@ motion_submitter: restriction_mode: A constant: true +motion_supporter: + id: *id_field + meeting_user_id: + type: relation + reference: meeting_user + to: meeting_user/motion_supporter_ids + restriction_mode: A + motion_id: + type: relation + reference: motion + to: motion/supporter_ids + equal_fields: meeting_id + restriction_mode: A + required: true + constant: true + meeting_id: + type: relation + reference: meeting + to: meeting/motion_supporter_ids + required: true + restriction_mode: A + constant: true + motion_editor: id: *id_field weight: From bad61264c58913d6f9c09fab0943765d92db1178 Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:40:27 +0100 Subject: [PATCH 126/142] Re-add test data for --rl:gr (#349) --- dev/sql/test_data.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/sql/test_data.sql b/dev/sql/test_data.sql index ac8d2f86..1ee8c943 100644 --- a/dev/sql/test_data.sql +++ b/dev/sql/test_data.sql @@ -92,6 +92,10 @@ INSERT INTO agenda_item_t (content_object_id, meeting_id) VALUES ('topic/1', 2); COMMIT; +--rl:gr organization.mediafile_ids:mediafile.owner_id +INSERT INTO mediafile_t (id, owner_id) +VALUES (1, 'organization/1'); + --rl:rl committee_ids:user_ids INSERT INTO nm_committee_manager_ids_user_t (committee_id, user_id) VALUES (1, 1); From bc648c8c9521b5dcf92577ebccbe6989c3268aba Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:26:27 +0100 Subject: [PATCH 127/142] Implement TextArrayField (#361) --- dev/sql/schema_relational.sql | 4 ++-- dev/src/generate_sql_schema.py | 4 ++++ dev/src/validate.py | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 1492966d..7a50c3c6 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '40ed6ee4a4afca95d6fbbcc0addd2d93' +-- MODELS_YML_CHECKSUM = 'cf9d9816258d1c3187d3dbc536233389' -- Function and meta table definitions @@ -1322,7 +1322,7 @@ CREATE TABLE history_position_t ( CREATE TABLE history_entry_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - entries varchar(256)[], + entries text[], original_model_id varchar(256), model_id varchar(100), model_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'user' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 3606f304..75cb6c69 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1481,6 +1481,10 @@ def get_foreign_table_from_to_or_reference( "pg_type": "integer[]", "method": GenerateCodeBlocks.get_schema_simple_types, }, + "text[]": { + "pg_type": "text[]", + "method": GenerateCodeBlocks.get_schema_simple_types, + }, "text": {"pg_type": "text", "method": GenerateCodeBlocks.get_schema_simple_types}, "relation": {"pg_type": "integer", "method": GenerateCodeBlocks.get_relation_type}, "relation-list": { diff --git a/dev/src/validate.py b/dev/src/validate.py index c3af6d88..5693543c 100644 --- a/dev/src/validate.py +++ b/dev/src/validate.py @@ -33,6 +33,7 @@ "number", "string[]", "number[]", + "text[]", "boolean", "JSON", "HTMLStrict", @@ -171,7 +172,7 @@ def check_field( ) valid_attributes = list(OPTIONAL_ATTRIBUTES) + required_attributes - if type == "string[]": + if type in ["string[]", "text[]"]: valid_attributes.append("items") if "items" in field and "enum" not in field["items"]: self.errors.append( @@ -249,7 +250,7 @@ def validate_value_for_type( self.errors.append( f"Value '{value}' for '{collectionfield}' is not a {type_str}." ) - elif type_str in ("string[]", "number[]"): + elif type_str in ("string[]", "number[]", "text[]"): if not isinstance(value, list): self.errors.append( f"Value '{value}' for '{collectionfield}' is not a {type_str}." From 5baa08e3f57be4d82863c80cd2ee51ea0d78f68e Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 16 Jan 2026 18:31:52 +0100 Subject: [PATCH 128/142] generate models.yml --- models.yml | 5205 ++++++++++++++++++++++++++-------------------------- 1 file changed, 2594 insertions(+), 2611 deletions(-) diff --git a/models.yml b/models.yml index 724dfd42..347b97ad 100644 --- a/models.yml +++ b/models.yml @@ -1,798 +1,369 @@ +# GENERATED FILE - DO NOT EDIT MANUALLY +# This file is automatically generated. Manual changes will be overwritten. --- -# Length of names: -# - field name: Their length is limited to 25 characters. There are still some -# fields with longer names, that has to be shortened -# Types: -# - Native data types: text, string (text with maxLength=256), number, boolean, JSON -# - HTMLStrict: A string with HTML content. -# - HTMLPermissive: A string with HTML content (with video tags). -# - float: Numbers that are expected to be non-integer. Formatted as in rfc7159. -# - decimal(): Decimal values represented as a string with X decimal places. -# At the moment we support only X == 6. -# - timestamp: Datetime as a unix timestamp. Why a number? This enables queries -# in the DB. And we do not need more precision than 1 second. -# - []: This indicates an arbitrary array of the given type. At the moment -# we support only some types. You can add JSON Schema properties for items -# using the extra property `items` -# - color: string that must match ^#[0-9a-f]{6}$ -# Relations: -# - We have the following types: `relation`, `relation-list`, `generic-relation` -# and `generic-relation-list`. All relation-types with a `list`-suffix are **always** realized as sql-statements, -# in case of n:m-relations with an intermediary table. -# - Non-generic relations: The simple syntax for such a field -# `to: /`. This is a reference to a collection-field, the "other" side of a relation. -# In the relational DB this will be a field in a view filled by generated sql. -# If necessary the sql can be written manually, see `sql`. -# `reference: `. The field `id` will be assumed. This will create a foreign key constraint. Therefore a `reference` is -# the indicator for a real field in the table and you cannot use it for relation types with a `list` suffix. -# For some time there may be also a `to`-property for the relation, but for schema generation -# the `reference` takes precedence over the `to`. -# `deferred: true`: this field get the "INITIALLY DEFERRED" on it's foreign key definition -# - Generic relations: The difference to non-generic relations is that you have a -# list of possible fields, so `to` can either hold multiple collections (if the -# field name is the same): -# to: -# collections: -# - agenda_item -# - assignment -# - ... -# field: tag_ids -# Or `to` can be a list of collection fields: -# to: -# - motion/option_ids -# - user/option_ids -# - poll_candidate_list/option_id -# For a generic-relation (not for generic-relation-list, see above), the attribute `reference` is a list of references: -# reference: -# - agenda_item -# - assignment -# The `sql`-attribute can only be used for relation-types in combination with a `to`-attribute. The `required` attribute -# is not allowed, because in hand written sql we can't check for. -# Currently implemented only for n:m-relations without generating an intermediate table. -# List of allowed and implemented relations -# Symbols: -# 1 = relation -# 1G = generic-relation -# n = relation-list -# nG = generic-relation-list -# r = reference, will result in a FIELD -# to = to, collection with field get's an automatic sql in a view, if not together with `reference` -# R = required -# The first tuple symbols the relation from-to, the 2nd FIELD or SQL and primary or not, only valid for lists -# The original of the following list is used in source code as `decision_list` -# ("1Gr", ""): (FieldSqlErrorType.FIELD, False), -# ("1GrR", ""): (FieldSqlErrorType.FIELD, False), -# ("1r", ""): (FieldSqlErrorType.FIELD, False), -# ("1rR", ""): (FieldSqlErrorType.FIELD, False), -# ("1t", "1GrR"): (FieldSqlErrorType.SQL, False), -# ("1t", "1r"): (FieldSqlErrorType.SQL, False), -# ("1t", "1rR"): (FieldSqlErrorType.SQL, False), -# ("1tR", "1Gr"): (FieldSqlErrorType.SQL, False), -# ("1tR", "1GrR"): (FieldSqlErrorType.SQL, False), -# ("nGt", "nt"): (FieldSqlErrorType.SQL, True), -# ("nr", ""): (FieldSqlErrorType.SQL, True), -# ("nt", "1Gr"): (FieldSqlErrorType.SQL, False), -# ("nt", "1GrR"): (FieldSqlErrorType.SQL, False), -# ("nt", "1r"): (FieldSqlErrorType.SQL, False), -# ("nt", "1rR"): (FieldSqlErrorType.SQL, False), -# ("nt", "nGt"): (FieldSqlErrorType.SQL, False), -# ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), -# ("ntR", "1r"): (FieldSqlErrorType.SQL, False), -# ("nts", "nts"): (FieldSqlErrorType.SQL, False), -# -# - on_delete: This fields determines what should happen with the foreign model if -# this model gets deleted. Possible values are: -# - SET_NULL (default): delete the id from the foreign key -# - PROTECT: if the foreign key is not empty, throw an error instead of -# deleting the object -# - CASCADE: also delete all models in this foreign key -# JSON Schema Properties: -# - You can add JSON Schema properties to the fields like `enum`, `description`, -# `items`, `maxLength` and `minimum` -# Additional properties: -# - The property `read_only` describes a field that can not directly be changed by an action. -# - The property `default` describes the default value that is used for new objects. -# - The property `required` describes that this field can not be null or an empty -# string. If this field is given it must have some content. On relation and generic-relation -# fields the value as to be an id of an existing object. -# - The property `equal_fields` describes fields that must have the same value in -# the instance and the related instance. -# Restriction Mode: -# The field `restriction_mode` is required for every field. It puts the field into a -# restriction group. See https://github.com/OpenSlides/OpenSlides/wiki/Restrictions-Overview - -_meta: # meta field to hold repeatedly used values - languages: &languages - - en - - de - - it - - es - - ru - - cs - - fr - ballot_paper_selection: &ballot_paper_selection - - NUMBER_OF_DELEGATES - - NUMBER_OF_ALL_PARTICIPANTS - - CUSTOM_NUMBER - poll_backends: &poll_backends - - long - - fast - onehundred_percent_bases: &onehundred_percent_bases - - "Y" - - "YN" - - "YNA" - - "N" - - "valid" - - "cast" - - "entitled" - - "entitled_present" - - "disabled" - id_field: &id_field +_meta: + languages: + - en + - de + - it + - es + - ru + - cs + - fr + ballot_paper_selection: + - NUMBER_OF_DELEGATES + - NUMBER_OF_ALL_PARTICIPANTS + - CUSTOM_NUMBER + poll_backends: + - long + - fast + onehundred_percent_bases: + - Y + - YN + - YNA + - N + - valid + - cast + - entitled + - entitled_present + - disabled + id_field: + type: number + restriction_mode: A + constant: true + required: true +action_worker: + id: type: number restriction_mode: A constant: true required: true - -organization: - id: *id_field name: type: string + required: true restriction_mode: A - description: - type: HTMLStrict + state: + type: string + required: true + enum: + - running + - end + - aborted restriction_mode: A - - # Settings (configurable by the client) - legal_notice: - type: text + created: + type: timestamp + required: true restriction_mode: A - privacy_policy: - type: text + timestamp: + type: timestamp + required: true restriction_mode: A - login_text: - type: text + result: + type: JSON restriction_mode: A - reset_password_verbose_errors: - type: boolean - restriction_mode: B - gender_ids: - type: relation-list - to: gender/organization_id - reference: gender + user_id: + type: number + description: Id of the calling user. If the action is called via internal route, + the value will be -1. + required: true restriction_mode: A - disable_forward_with_attachments: - type: boolean + constant: true +agenda_item: + id: + type: number restriction_mode: A - - # Configuration (only for the server owner) - enable_electronic_voting: - type: boolean - restriction_mode: B - enable_chat: - type: boolean + constant: true + required: true + item_number: + type: string restriction_mode: A - limit_of_meetings: - type: number - description: Maximum of active meetings for the whole organization. 0 means no limitation at all - restriction_mode: C - default: 0 - minimum: 0 - limit_of_users: - type: number - description: Maximum of active users for the whole organization. 0 means no limitation at all + comment: + type: string restriction_mode: C - default: 0 - minimum: 0 - default_language: + closed: + type: boolean + default: false + restriction_mode: A + type: type: string + enum: + - common + - internal + - hidden + default: common restriction_mode: A - enum: *languages - default: en - require_duplicate_from: + duration: + type: number + description: Given in seconds + minimum: 0 + restriction_mode: B + is_internal: type: boolean + description: Calculated by the server + read_only: true restriction_mode: A - enable_anonymous: + is_hidden: type: boolean + description: Calculated by the server + read_only: true restriction_mode: A - - # Saml settings - saml_enabled: - type: boolean + level: + type: number + description: Calculated by the server + read_only: true restriction_mode: A - saml_login_button_text: - type: string - default: SAML login + weight: + type: number restriction_mode: A - saml_attr_mapping: - type: JSON - restriction_mode: D - saml_metadata_idp: - type: text - restriction_mode: D - saml_metadata_sp: - type: text - restriction_mode: D - saml_private_key: - type: text - restriction_mode: D - committee_ids: - type: relation-list - to: committee/organization_id - restriction_mode: B - reference: committee - active_meeting_ids: - type: relation-list + content_object_id: + type: generic-relation + reference: + - motion + - motion_block + - assignment + - topic + to: + collections: + - motion + - motion_block + - assignment + - topic + field: agenda_item_id + required: true + equal_fields: meeting_id restriction_mode: A - to: meeting/is_active_in_organization_id - archived_meeting_ids: + constant: true + parent_id: + type: relation + reference: agenda_item + to: agenda_item/child_ids + equal_fields: meeting_id + restriction_mode: A + child_ids: type: relation-list - restriction_mode: B - to: meeting/is_archived_in_organization_id - template_meeting_ids: + to: agenda_item/parent_id + equal_fields: meeting_id + restriction_mode: A + tag_ids: type: relation-list - to: meeting/template_for_organization_id + to: tag/tagged_ids + equal_fields: meeting_id restriction_mode: A - organization_tag_ids: + projection_ids: type: relation-list + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE restriction_mode: A - to: organization_tag/organization_id - reference: organization_tag - theme_id: + meeting_id: type: relation + reference: meeting + to: meeting/agenda_item_ids required: true restriction_mode: A - to: theme/theme_for_organization_id - reference: theme - theme_ids: - type: relation-list - restriction_mode: A - to: theme/organization_id - reference: theme - mediafile_ids: - type: relation-list - to: mediafile/owner_id - on_delete: CASCADE + constant: true +assignment: + id: + type: number restriction_mode: A - published_mediafile_ids: - type: relation-list - to: mediafile/published_to_meetings_in_organization_id - restriction_mode: E - user_ids: - type: relation-list - restriction_mode: C - to: user/organization_id - reference: user - users_email_sender: + constant: true + required: true + title: type: string - default: OpenSlides + required: true restriction_mode: A - users_email_replyto: - type: string + description: + type: HTMLStrict restriction_mode: A - users_email_subject: + open_posts: + type: number + minimum: 0 + default: 0 + restriction_mode: A + phase: type: string - default: OpenSlides access data + enum: + - search + - voting + - finished + default: search restriction_mode: A - users_email_body: + default_poll_description: type: text - default: >- - Dear {name}, - - - this is your personal OpenSlides login: - - - {url} - - Username: {username} - - Password: {password} - - - - This email was generated automatically. restriction_mode: A - url: - type: string - default: https://example.com + number_poll_candidates: + type: boolean restriction_mode: A - -user: - id: *id_field - username: - type: string + sequential_number: + type: number + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true required: true - restriction_mode: B - member_number: - type: string - restriction_mode: B - saml_id: - type: string - minLength: 1 - restriction_mode: B - description: unique-key from IdP for SAML login - pronoun: - type: string - maxLength: 32 - restriction_mode: A - title: - type: string - restriction_mode: A - first_name: - type: string - restriction_mode: A - last_name: - type: string - restriction_mode: A - is_active: - type: boolean - default: true - restriction_mode: B - is_physical_person: - type: boolean - default: true - restriction_mode: A - password: - type: string - restriction_mode: G - default_password: - type: string - restriction_mode: H - can_change_own_password: - type: boolean - default: true - restriction_mode: D - email: - type: string - restriction_mode: B - default_vote_weight: - type: decimal(6) - default: "1.000000" - minimum: "0.000001" - restriction_mode: A - last_email_sent: - type: timestamp - restriction_mode: B - is_demo_user: - type: boolean - restriction_mode: A - last_login: - type: timestamp - restriction_mode: A - read_only: true - external: - type: boolean - restriction_mode: E - gender_id: - type: relation - to: gender/user_ids restriction_mode: A - reference: gender - - # Organization, meeting and committee - organization_management_level: - type: string - description: Hierarchical permission level for the whole organization. - enum: - - superadmin - - can_manage_organization - - can_manage_users - restriction_mode: B - is_present_in_meeting_ids: + constant: true + candidate_ids: type: relation-list - to: meeting/present_user_ids + to: assignment_candidate/assignment_id + on_delete: CASCADE + equal_fields: meeting_id restriction_mode: A - - # Calculates all committee's where the user is - # - committee-manager (= committees in committee_management_ids) - # - is member (= is in a group) of a meeting in the committee - # - or has his home committee - committee_ids: - type: relation-list - to: committee/user_ids - restriction_mode: E - read_only: true - description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" - sql: | - ( - SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) - FROM ( - -- Select committee_ids from meetings the user is part of - SELECT m.committee_id - FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - INNER JOIN meeting_t AS m ON m.id = mu.meeting_id - WHERE mu.user_id = u.id - - UNION - - -- Select committee_ids from committee managers - SELECT cmu.committee_id - FROM nm_committee_manager_ids_user_t cmu - WHERE cmu.user_id = u.id - - UNION - - -- Select home_committee_id from user - SELECT u.home_committee_id - WHERE u.home_committee_id IS NOT NULL - ) _ - ) AS committee_ids - - # committee specific permissions - committee_management_ids: - type: relation-list - to: committee/manager_ids - restriction_mode: E - - meeting_user_ids: + poll_ids: type: relation-list - to: meeting_user/user_id + to: poll/content_object_id + on_delete: CASCADE + equal_fields: meeting_id restriction_mode: A + agenda_item_id: + type: relation + to: agenda_item/content_object_id on_delete: CASCADE - poll_voted_ids: - type: relation-list - to: poll/voted_ids + equal_fields: meeting_id restriction_mode: A - option_ids: - type: relation-list - to: option/content_object_id - reference: option + list_of_speakers_id: + type: relation + to: list_of_speakers/content_object_id + required: true + on_delete: CASCADE + equal_fields: meeting_id restriction_mode: A - vote_ids: + constant: true + tag_ids: type: relation-list - to: vote/user_id - reference: vote + to: tag/tagged_ids + equal_fields: meeting_id restriction_mode: A - delegated_vote_ids: + attachment_meeting_mediafile_ids: type: relation-list - to: vote/delegated_user_id - reference: vote + to: meeting_mediafile/attachment_ids + equal_fields: meeting_id restriction_mode: A - poll_candidate_ids: + projection_ids: type: relation-list - to: poll_candidate/user_id - reference: poll_candidate + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE restriction_mode: A - home_committee_id: + meeting_id: type: relation - to: committee/native_user_ids - restriction_mode: E - reference: committee - - history_position_ids: - type: relation-list - to: history_position/user_id + reference: meeting + to: meeting/assignment_ids + required: true restriction_mode: A + constant: true history_entry_ids: type: relation-list to: history_entry/model_id restriction_mode: A - - meeting_ids: - type: relation-list - description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. - read_only: true - sql: | - ( - SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - WHERE mu.user_id = u.id - ) AS meeting_ids - to: meeting/user_ids - restriction_mode: E - organization_id: - type: relation - reference: organization - to: organization/user_ids - required: True - restriction_mode: F - constant: true - -meeting_user: - id: *id_field - - comment: - type: HTMLStrict - restriction_mode: D - number: - type: string - restriction_mode: A - about_me: - type: HTMLStrict +assignment_candidate: + id: + type: number restriction_mode: A - vote_weight: - type: decimal(6) - minimum: "0.000001" + constant: true + required: true + weight: + type: number + default: 10000 restriction_mode: A - locked_out: - type: boolean - restriction_mode: E - - user_id: + assignment_id: type: relation - reference: user - to: user/meeting_user_ids + reference: assignment + to: assignment/candidate_ids + equal_fields: meeting_id + restriction_mode: A required: true - restriction_mode: C + constant: true + meeting_user_id: + type: relation + reference: meeting_user + to: meeting_user/assignment_candidate_ids + restriction_mode: A constant: true meeting_id: type: relation reference: meeting - to: meeting/meeting_user_ids + to: meeting/assignment_candidate_ids required: true - restriction_mode: C + restriction_mode: A constant: true - - personal_note_ids: - type: relation-list - to: personal_note/meeting_user_id - equal_fields: meeting_id - on_delete: CASCADE - restriction_mode: B - speaker_ids: - type: relation-list - to: speaker/meeting_user_id - equal_fields: meeting_id +chat_group: + id: + type: number restriction_mode: A - motion_supporter_ids: - type: relation-list - to: motion_supporter/meeting_user_id - equal_fields: meeting_id + constant: true + required: true + name: + type: string + required: true restriction_mode: A - motion_editor_ids: - type: relation-list - to: motion_editor/meeting_user_id - equal_fields: meeting_id + weight: + type: number + default: 10000 restriction_mode: A - motion_working_group_speaker_ids: + chat_message_ids: type: relation-list - to: motion_working_group_speaker/meeting_user_id + to: chat_message/chat_group_id equal_fields: meeting_id restriction_mode: A - motion_submitter_ids: + on_delete: CASCADE + read_group_ids: type: relation-list - to: motion_submitter/meeting_user_id + to: group/read_chat_group_ids equal_fields: meeting_id restriction_mode: A - assignment_candidate_ids: + write_group_ids: type: relation-list - to: assignment_candidate/meeting_user_id + to: group/write_chat_group_ids equal_fields: meeting_id restriction_mode: A - vote_delegated_to_id: + meeting_id: type: relation - reference: meeting_user - to: meeting_user/vote_delegations_from_ids - equal_fields: meeting_id - restriction_mode: A - vote_delegations_from_ids: - type: relation-list - to: meeting_user/vote_delegated_to_id - equal_fields: meeting_id + reference: meeting + to: meeting/chat_group_ids + required: true restriction_mode: A - chat_message_ids: - type: relation-list - to: chat_message/meeting_user_id - equal_fields: meeting_id + constant: true +chat_message: + id: + type: number restriction_mode: A - group_ids: - type: relation-list - to: group/meeting_user_ids - equal_fields: meeting_id - restriction_mode: A - structure_level_ids: - type: relation-list - equal_fields: meeting_id - restriction_mode: A - to: structure_level/meeting_user_ids - -gender: - id: *id_field - name: - type: string - required: True - restriction_mode: A - description: unique - organization_id: - type: relation - required: True - to: organization/gender_ids - restriction_mode: A - reference: organization - user_ids: - type: relation-list - to: user/gender_id - restriction_mode: A - reference: user - -organization_tag: - id: *id_field - name: - type: string + constant: true + required: true + content: + type: HTMLStrict required: true restriction_mode: A - color: - type: color + created: + type: timestamp required: true restriction_mode: A - - tagged_ids: - type: generic-relation-list - to: - collections: - - committee - - meeting - field: organization_tag_ids + meeting_user_id: + type: relation + reference: meeting_user + to: meeting_user/chat_message_ids restriction_mode: A - organization_id: + constant: true + chat_group_id: type: relation - reference: organization - to: organization/organization_tag_ids + reference: chat_group + to: chat_group/chat_message_ids restriction_mode: A required: true - -theme: - id: *id_field - name: - restriction_mode: A - type: string + constant: true + meeting_id: + type: relation + reference: meeting + to: meeting/chat_message_ids required: true - accent_100: - restriction_mode: A - type: color - accent_200: - restriction_mode: A - type: color - accent_300: - restriction_mode: A - type: color - accent_400: - restriction_mode: A - type: color - accent_50: - restriction_mode: A - type: color - accent_500: - restriction_mode: A - type: color - default: "#2196f3" - accent_600: - restriction_mode: A - type: color - accent_700: - restriction_mode: A - type: color - accent_800: - restriction_mode: A - type: color - accent_900: - restriction_mode: A - type: color - accent_a100: - restriction_mode: A - type: color - accent_a200: - restriction_mode: A - type: color - accent_a400: - restriction_mode: A - type: color - accent_a700: - restriction_mode: A - type: color - primary_100: - restriction_mode: A - type: color - primary_200: - restriction_mode: A - type: color - primary_300: - restriction_mode: A - type: color - primary_400: - restriction_mode: A - type: color - primary_50: - restriction_mode: A - type: color - primary_500: - restriction_mode: A - type: color - default: "#317796" - primary_600: - restriction_mode: A - type: color - primary_700: - restriction_mode: A - type: color - primary_800: - restriction_mode: A - type: color - primary_900: - restriction_mode: A - type: color - primary_a100: - restriction_mode: A - type: color - primary_a200: - restriction_mode: A - type: color - primary_a400: - restriction_mode: A - type: color - primary_a700: - restriction_mode: A - type: color - warn_100: - restriction_mode: A - type: color - warn_200: - restriction_mode: A - type: color - warn_300: - restriction_mode: A - type: color - warn_400: - restriction_mode: A - type: color - warn_50: - restriction_mode: A - type: color - warn_500: - restriction_mode: A - type: color - default: "#f06400" - warn_600: - restriction_mode: A - type: color - warn_700: - restriction_mode: A - type: color - warn_800: - restriction_mode: A - type: color - warn_900: - restriction_mode: A - type: color - warn_a100: - restriction_mode: A - type: color - warn_a200: - restriction_mode: A - type: color - warn_a400: - restriction_mode: A - type: color - warn_a700: - restriction_mode: A - type: color - headbar: - restriction_mode: A - type: color - "yes": restriction_mode: A - type: color - "no": - restriction_mode: A - type: color - abstain: - restriction_mode: A - type: color - theme_for_organization_id: - restriction_mode: A - to: organization/theme_id - type: relation - organization_id: - type: relation - reference: organization - to: organization/theme_ids + constant: true +committee: + id: + type: number restriction_mode: A + constant: true required: true - -committee: - id: *id_field name: type: string required: true @@ -819,36 +390,21 @@ committee: to: user/committee_ids restriction_mode: A read_only: true - description: "Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee" - sql: | - ( - SELECT array_agg(DISTINCT user_id ORDER BY user_id) - FROM ( - -- Select user_ids from committees meetings - SELECT mu.user_id - FROM meeting_t AS m - INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - WHERE m.committee_id = c.id - - UNION - - -- Select user_ids from committee managers - SELECT cmu.user_id - FROM nm_committee_manager_ids_user_t cmu - WHERE cmu.committee_id = c.id - - UNION - - -- Select user_id from home committees - SELECT u.id FROM user_t u WHERE u.home_committee_id = c.id - ) _ - ) AS user_ids + description: 'Calculated field: All users which are in a group of a meeting, belonging + to the committee or beeing manager of the committee' + sql: "(\n SELECT array_agg(DISTINCT user_id ORDER BY user_id)\n FROM (\n \ + \ -- Select user_ids from committees meetings\n SELECT mu.user_id\n FROM\ + \ meeting_t AS m\n INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id\n\ + \ INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id\n\ + \ WHERE m.committee_id = c.id\n\n UNION\n\n -- Select user_ids from\ + \ committee managers\n SELECT cmu.user_id\n FROM nm_committee_manager_ids_user_t\ + \ cmu\n WHERE cmu.committee_id = c.id\n\n UNION\n\n -- Select user_id\ + \ from home committees\n SELECT u.id FROM user_t u WHERE u.home_committee_id\ + \ = c.id\n ) _\n) AS user_ids\n" manager_ids: type: relation-list to: user/committee_management_ids restriction_mode: B - parent_id: type: relation to: committee/child_ids @@ -858,11 +414,11 @@ committee: type: relation-list to: committee/parent_id restriction_mode: A - all_parent_ids: # Calculated: All parents, grandparents, etc. of this motion. + all_parent_ids: type: relation-list to: committee/all_child_ids restriction_mode: A - all_child_ids: # Calculated: All children, grandchildren, etc. of this motion. + all_child_ids: type: relation-list to: committee/all_parent_ids restriction_mode: A @@ -889,41 +445,439 @@ committee: required: true restriction_mode: A constant: true - -meeting: - id: *id_field - external_id: - type: string - restriction_mode: F - description: unique in committee - welcome_title: - type: string - default: Welcome to OpenSlides - restriction_mode: C - welcome_text: - type: HTMLPermissive - default: Space for your welcome text. - restriction_mode: C - - # General +gender: + id: + type: number + restriction_mode: A + constant: true + required: true name: type: string - maxLength: 100 - default: OpenSlides - restriction_mode: A required: true - is_active_in_organization_id: - type: relation - reference: organization - to: organization/active_meeting_ids - restriction_mode: F - description: Backrelation and boolean flag at once - is_archived_in_organization_id: + restriction_mode: A + description: unique + organization_id: type: relation + required: true + to: organization/gender_ids + restriction_mode: A reference: organization - to: organization/archived_meeting_ids - restriction_mode: F - description: Backrelation and boolean flag at once + user_ids: + type: relation-list + to: user/gender_id + restriction_mode: A + reference: user +group: + id: + type: number + restriction_mode: A + constant: true + required: true + external_id: + type: string + restriction_mode: A + description: unique in meeting + name: + type: string + required: true + restriction_mode: A + permissions: + type: string[] + items: + enum: + - agenda_item.can_manage + - agenda_item.can_see + - agenda_item.can_see_internal + - assignment.can_manage + - assignment.can_manage_polls + - assignment.can_nominate_other + - assignment.can_nominate_self + - assignment.can_see + - chat.can_manage + - list_of_speakers.can_be_speaker + - list_of_speakers.can_manage + - list_of_speakers.can_see + - list_of_speakers.can_manage_moderator_notes + - list_of_speakers.can_see_moderator_notes + - mediafile.can_manage + - mediafile.can_see + - meeting.can_manage_logos_and_fonts + - meeting.can_manage_settings + - meeting.can_see_autopilot + - meeting.can_see_frontpage + - meeting.can_see_history + - meeting.can_see_livestream + - motion.can_create + - motion.can_create_amendments + - motion.can_forward + - motion.can_manage + - motion.can_manage_metadata + - motion.can_manage_polls + - motion.can_see + - motion.can_see_internal + - motion.can_see_origin + - motion.can_support + - poll.can_manage + - poll.can_see_progress + - projector.can_manage + - projector.can_see + - tag.can_manage + - user.can_manage + - user.can_manage_presence + - user.can_see_sensitive_data + - user.can_see + - user.can_update + - user.can_edit_own_delegation + restriction_mode: A + weight: + type: number + restriction_mode: A + meeting_user_ids: + type: relation-list + to: meeting_user/group_ids + restriction_mode: A + equal_fields: meeting_id + default_group_for_meeting_id: + type: relation + to: meeting/default_group_id + on_delete: PROTECT + restriction_mode: A + admin_group_for_meeting_id: + type: relation + to: meeting/admin_group_id + on_delete: PROTECT + restriction_mode: A + anonymous_group_for_meeting_id: + type: relation + to: meeting/anonymous_group_id + on_delete: PROTECT + restriction_mode: A + meeting_mediafile_access_group_ids: + type: relation-list + to: meeting_mediafile/access_group_ids + equal_fields: meeting_id + restriction_mode: A + meeting_mediafile_inherited_access_group_ids: + type: relation-list + to: meeting_mediafile/inherited_access_group_ids + description: Calculated field. + read_only: true + restriction_mode: A + read_comment_section_ids: + type: relation-list + to: motion_comment_section/read_group_ids + equal_fields: meeting_id + restriction_mode: A + write_comment_section_ids: + type: relation-list + to: motion_comment_section/write_group_ids + equal_fields: meeting_id + restriction_mode: A + read_chat_group_ids: + type: relation-list + to: chat_group/read_group_ids + equal_fields: meeting_id + restriction_mode: A + write_chat_group_ids: + type: relation-list + to: chat_group/write_group_ids + equal_fields: meeting_id + restriction_mode: A + poll_ids: + type: relation-list + to: poll/entitled_group_ids + equal_fields: meeting_id + restriction_mode: A + used_as_motion_poll_default_id: + type: relation + reference: meeting + to: meeting/motion_poll_default_group_ids + restriction_mode: A + used_as_assignment_poll_default_id: + type: relation + reference: meeting + to: meeting/assignment_poll_default_group_ids + restriction_mode: A + used_as_topic_poll_default_id: + type: relation + reference: meeting + to: meeting/topic_poll_default_group_ids + restriction_mode: A + used_as_poll_default_id: + type: relation + reference: meeting + to: meeting/poll_default_group_ids + restriction_mode: A + meeting_id: + type: relation + reference: meeting + to: meeting/group_ids + required: true + restriction_mode: A + constant: true +history_entry: + id: + type: number + restriction_mode: A + constant: true + required: true + entries: + type: text[] + restriction_mode: A + original_model_id: + type: string + restriction_mode: A + constant: true + model_id: + type: generic-relation + to: + collections: + - user + - motion + - assignment + field: history_entry_ids + restriction_mode: A + reference: + - user + - motion + - assignment + position_id: + type: relation + to: history_position/entry_ids + required: true + restriction_mode: A + constant: true + reference: history_position + meeting_id: + type: relation + to: meeting/relevant_history_entry_ids + restriction_mode: A + reference: meeting +history_position: + id: + type: number + restriction_mode: A + constant: true + required: true + timestamp: + type: timestamp + restriction_mode: A + read_only: true + original_user_id: + type: number + restriction_mode: A + constant: true + user_id: + type: relation + to: user/history_position_ids + restriction_mode: A + reference: user + entry_ids: + type: relation-list + to: history_entry/position_id + restriction_mode: A + on_delete: CASCADE +import_preview: + id: + type: number + restriction_mode: A + constant: true + required: true + name: + type: string + required: true + enum: + - account + - participant + - topic + - committee + - motion + restriction_mode: A + state: + type: string + required: true + enum: + - warning + - error + - done + restriction_mode: A + created: + type: timestamp + required: true + restriction_mode: A + result: + type: JSON + restriction_mode: A +list_of_speakers: + id: + type: number + restriction_mode: A + constant: true + required: true + closed: + type: boolean + default: false + restriction_mode: A + sequential_number: + type: number + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true + required: true + restriction_mode: A + constant: true + moderator_notes: + type: HTMLStrict + restriction_mode: B + content_object_id: + type: generic-relation + reference: + - motion + - motion_block + - assignment + - topic + - meeting_mediafile + to: + collections: + - motion + - motion_block + - assignment + - topic + - meeting_mediafile + field: list_of_speakers_id + required: true + equal_fields: meeting_id + restriction_mode: A + constant: true + speaker_ids: + type: relation-list + to: speaker/list_of_speakers_id + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + structure_level_list_of_speakers_ids: + type: relation-list + restriction_mode: A + equal_fields: meeting_id + to: structure_level_list_of_speakers/list_of_speakers_id + on_delete: CASCADE + projection_ids: + type: relation-list + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE + restriction_mode: A + meeting_id: + type: relation + reference: meeting + to: meeting/list_of_speakers_ids + required: true + restriction_mode: A + constant: true +mediafile: + id: + type: number + restriction_mode: A + constant: true + required: true + title: + type: string + description: Title and parent_id must be unique. + restriction_mode: A + is_directory: + type: boolean + restriction_mode: A + filesize: + type: number + description: In bytes, not the human readable format anymore. + read_only: true + restriction_mode: A + filename: + type: string + description: The uploaded filename. Will be used for downloading. Only writeable + on create. + restriction_mode: A + mimetype: + type: string + restriction_mode: A + pdf_information: + type: JSON + restriction_mode: A + create_timestamp: + type: timestamp + restriction_mode: A + token: + type: string + restriction_mode: A + published_to_meetings_in_organization_id: + type: relation + to: organization/published_mediafile_ids + reference: organization + restriction_mode: A + parent_id: + type: relation + reference: mediafile + to: mediafile/child_ids + equal_fields: owner_id + restriction_mode: A + child_ids: + type: relation-list + to: mediafile/parent_id + equal_fields: owner_id + restriction_mode: A + owner_id: + type: generic-relation + to: + - meeting/mediafile_ids + - organization/mediafile_ids + reference: + - meeting + - organization + restriction_mode: A + required: true + constant: true + meeting_mediafile_ids: + type: relation-list + to: meeting_mediafile/mediafile_id + on_delete: CASCADE + restriction_mode: A +meeting: + id: + type: number + restriction_mode: A + constant: true + required: true + external_id: + type: string + restriction_mode: F + description: unique in committee + welcome_title: + type: string + default: Welcome to OpenSlides + restriction_mode: C + welcome_text: + type: HTMLPermissive + default: Space for your welcome text. + restriction_mode: C + name: + type: string + maxLength: 100 + default: OpenSlides + restriction_mode: A + required: true + is_active_in_organization_id: + type: relation + reference: organization + to: organization/active_meeting_ids + restriction_mode: F + description: Backrelation and boolean flag at once + is_archived_in_organization_id: + type: relation + reference: organization + to: organization/archived_meeting_ids + restriction_mode: F + description: Backrelation and boolean flag at once description: type: string maxLength: 100 @@ -946,11 +900,16 @@ meeting: language: type: string restriction_mode: F - enum: *languages - default: "en" + enum: + - en + - de + - it + - es + - ru + - cs + - fr + default: en constant: true - - # Configuration (only for the server owner) jitsi_domain: type: string restriction_mode: E @@ -960,8 +919,6 @@ meeting: jitsi_room_password: type: string restriction_mode: E - - # System template_for_organization_id: type: relation reference: organization @@ -969,24 +926,22 @@ meeting: restriction_mode: B enable_anonymous: type: boolean - default: False + default: false restriction_mode: F custom_translations: type: JSON restriction_mode: B - - # Jitsi/Livestream settings conference_show: type: boolean - default: False + default: false restriction_mode: C conference_auto_connect: type: boolean - default: False + default: false restriction_mode: C conference_los_restriction: type: boolean - default: True + default: true restriction_mode: C conference_stream_url: type: string @@ -996,11 +951,11 @@ meeting: restriction_mode: C conference_open_microphone: type: boolean - default: False + default: false restriction_mode: C conference_open_video: type: boolean - default: False + default: false restriction_mode: C conference_auto_connect_next_speakers: type: number @@ -1008,22 +963,22 @@ meeting: restriction_mode: C conference_enable_helpdesk: type: boolean - default: False + default: false restriction_mode: C applause_enable: type: boolean - default: False + default: false restriction_mode: C applause_type: type: string enum: - - applause-type-bar - - applause-type-particles + - applause-type-bar + - applause-type-particles default: applause-type-bar restriction_mode: C applause_show_level: type: boolean - default: False + default: false restriction_mode: C applause_min_amount: type: number @@ -1043,8 +998,6 @@ meeting: applause_particle_image_url: type: string restriction_mode: C - - # Projector countdown projector_countdown_default_time: type: number default: 60 @@ -1056,33 +1009,31 @@ meeting: default: 0 required: true restriction_mode: B - - # Exports export_csv_encoding: type: string enum: - - utf-8 - - iso-8859-15 + - utf-8 + - iso-8859-15 default: utf-8 restriction_mode: B export_csv_separator: type: string - default: ";" + default: ; restriction_mode: B export_pdf_pagenumber_alignment: type: string enum: - - left - - right - - center + - left + - right + - center default: center restriction_mode: B export_pdf_fontsize: type: number enum: - - 10 - - 11 - - 12 + - 10 + - 11 + - 12 default: 10 restriction_mode: B export_pdf_line_height: @@ -1113,19 +1064,17 @@ meeting: export_pdf_pagesize: type: string enum: - - A4 - - A5 + - A4 + - A5 default: A4 restriction_mode: B - - # Agenda agenda_show_subtitles: type: boolean - default: False + default: false restriction_mode: B agenda_enable_numbering: type: boolean - default: True + default: true restriction_mode: B agenda_number_prefix: type: string @@ -1134,37 +1083,35 @@ meeting: agenda_numeral_system: type: string enum: - - arabic - - roman + - arabic + - roman default: arabic restriction_mode: B agenda_item_creation: type: string enum: - - always - - never - - default_yes - - default_no + - always + - never + - default_yes + - default_no default: default_no restriction_mode: B agenda_new_items_default_visibility: type: string enum: - - common - - internal - - hidden + - common + - internal + - hidden default: internal restriction_mode: B agenda_show_internal_items_on_projector: type: boolean - default: False + default: false restriction_mode: B agenda_show_topic_navigation_on_detail_view: type: boolean - default: False + default: false restriction_mode: B - - # List of speakers list_of_speakers_amount_last_on_projector: type: number minimum: -1 @@ -1177,59 +1124,59 @@ meeting: restriction_mode: B list_of_speakers_couple_countdown: type: boolean - default: True + default: true restriction_mode: B list_of_speakers_show_amount_of_speakers_on_slide: type: boolean - default: True + default: true restriction_mode: B list_of_speakers_present_users_only: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_show_first_contribution: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_hide_contribution_count: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_allow_multiple_speakers: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_enable_point_of_order_speakers: type: boolean - default: True + default: true restriction_mode: B list_of_speakers_can_create_point_of_order_for_others: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_enable_point_of_order_categories: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_closing_disables_point_of_order: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_enable_pro_contra_speech: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_can_set_contribution_self: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_speaker_note_for_everyone: type: boolean - default: True + default: true restriction_mode: B list_of_speakers_initially_closed: type: boolean - default: False + default: false restriction_mode: B list_of_speakers_default_structure_level_time: type: number @@ -1243,8 +1190,6 @@ meeting: type: number restriction_mode: B description: 0 disables intervention speakers. - - # Motions motions_default_workflow_id: type: relation reference: motion_workflow @@ -1259,14 +1204,14 @@ meeting: restriction_mode: B motions_preamble: type: text - default: "The assembly may decide:" + default: 'The assembly may decide:' restriction_mode: B motions_default_line_numbering: type: string enum: - - outside - - inline - - none + - outside + - inline + - none default: outside restriction_mode: A motions_line_length: @@ -1276,43 +1221,43 @@ meeting: restriction_mode: A motions_reason_required: type: boolean - default: False + default: false restriction_mode: B motions_origin_motion_toggle_default: type: boolean - default: False + default: false restriction_mode: B motions_enable_origin_motion_display: type: boolean - default: False + default: false restriction_mode: B motions_enable_text_on_projector: type: boolean - default: True + default: true restriction_mode: B motions_enable_reason_on_projector: type: boolean - default: False + default: false restriction_mode: B motions_enable_sidebox_on_projector: type: boolean - default: False + default: false restriction_mode: B motions_enable_recommendation_on_projector: type: boolean - default: True + default: true restriction_mode: B motions_hide_metadata_background: type: boolean - default: False + default: false restriction_mode: B motions_show_referring_motions: type: boolean - default: True + default: true restriction_mode: B motions_show_sequential_number: type: boolean - default: True + default: true restriction_mode: B motions_create_enable_additional_submitter_text: type: boolean @@ -1327,25 +1272,25 @@ meeting: motions_recommendation_text_mode: type: string enum: - - original - - changed - - diff - - agreed + - original + - changed + - diff + - agreed default: diff restriction_mode: B motions_default_sorting: type: string enum: - - number - - weight + - number + - weight default: number restriction_mode: B motions_number_type: type: string enum: - - per_category - - serially_numbered - - manually + - per_category + - serially_numbered + - manually default: per_category restriction_mode: B motions_number_min_digits: @@ -1354,35 +1299,35 @@ meeting: restriction_mode: B motions_number_with_blank: type: boolean - default: False + default: false restriction_mode: B motions_amendments_enabled: type: boolean - default: True + default: true restriction_mode: B motions_amendments_in_main_list: type: boolean - default: True + default: true restriction_mode: B motions_amendments_of_amendments: type: boolean - default: False + default: false restriction_mode: B motions_amendments_prefix: type: string - default: "-Ä" + default: -Ä restriction_mode: B motions_amendments_text_mode: type: string enum: - - freestyle - - fulltext - - paragraph + - freestyle + - fulltext + - paragraph default: paragraph restriction_mode: B motions_amendments_multiple_paragraphs: type: boolean - default: True + default: true restriction_mode: B motions_supporters_min_amount: type: number @@ -1404,11 +1349,11 @@ meeting: restriction_mode: B motions_export_submitter_recommendation: type: boolean - default: True + default: true restriction_mode: B motions_export_follow_recommendation: type: boolean - default: False + default: false restriction_mode: B motions_enable_restricted_editor_for_manager: type: boolean @@ -1416,11 +1361,12 @@ meeting: motions_enable_restricted_editor_for_non_manager: type: boolean restriction_mode: B - - # Motion poll motion_poll_ballot_paper_selection: type: string - enum: *ballot_paper_selection + enum: &id001 + - NUMBER_OF_DELEGATES + - NUMBER_OF_ALL_PARTICIPANTS + - CUSTOM_NUMBER default: CUSTOM_NUMBER restriction_mode: B motion_poll_ballot_paper_number: @@ -1437,7 +1383,16 @@ meeting: restriction_mode: B motion_poll_default_onehundred_percent_base: type: string - enum: *onehundred_percent_bases + enum: &id002 + - Y + - YN + - YNA + - N + - valid + - cast + - entitled + - entitled_present + - disabled default: YNA restriction_mode: B motion_poll_default_group_ids: @@ -1446,14 +1401,16 @@ meeting: restriction_mode: B motion_poll_default_backend: type: string - enum: *poll_backends + enum: &id003 + - long + - fast default: fast restriction_mode: B motion_poll_projection_name_order_first: type: string enum: - - first_name - - last_name + - first_name + - last_name default: last_name required: true restriction_mode: B @@ -1462,8 +1419,6 @@ meeting: default: 6 required: true restriction_mode: B - - # poll list election poll_candidate_list_ids: type: relation-list to: poll_candidate_list/meeting_id @@ -1474,8 +1429,6 @@ meeting: to: poll_candidate/meeting_id reference: poll_candidate restriction_mode: B - - # Users meeting_user_ids: type: relation-list to: meeting_user/meeting_id @@ -1483,15 +1436,15 @@ meeting: on_delete: CASCADE users_enable_presence_view: type: boolean - default: False + default: false restriction_mode: B users_enable_vote_weight: type: boolean - default: False + default: false restriction_mode: B users_allow_self_set_present: type: boolean - default: True + default: true restriction_mode: B users_pdf_welcometitle: type: string @@ -1499,7 +1452,7 @@ meeting: restriction_mode: B users_pdf_welcometext: type: text - default: "[Place for your welcome and help text.]" + default: '[Place for your welcome and help text.]' restriction_mode: B users_pdf_wlan_ssid: type: string @@ -1511,10 +1464,10 @@ meeting: type: string default: WPA enum: - - "" - - WEP - - WPA - - nopass + - '' + - WEP + - WPA + - nopass restriction_mode: B users_email_sender: type: string @@ -1529,8 +1482,7 @@ meeting: restriction_mode: B users_email_body: type: text - default: >- - Dear {name}, + default: 'Dear {name}, this is your personal OpenSlides login: @@ -1544,7 +1496,7 @@ meeting: - This email was generated automatically. + This email was generated automatically.' restriction_mode: B users_enable_vote_delegations: type: boolean @@ -1561,8 +1513,6 @@ meeting: users_forbid_delegator_to_vote: type: boolean restriction_mode: B - - # Assignments assignments_export_title: type: string default: Elections @@ -1570,11 +1520,9 @@ meeting: assignments_export_preamble: type: text restriction_mode: B - - # Assignment polls assignment_poll_ballot_paper_selection: type: string - enum: *ballot_paper_selection + enum: *id001 default: CUSTOM_NUMBER restriction_mode: B assignment_poll_ballot_paper_number: @@ -1583,15 +1531,15 @@ meeting: restriction_mode: B assignment_poll_add_candidates_to_list_of_speakers: type: boolean - default: False + default: false restriction_mode: B assignment_poll_enable_max_votes_per_option: type: boolean - default: False + default: false restriction_mode: B assignment_poll_sort_poll_result_by_votes: type: boolean - default: True + default: true restriction_mode: B assignment_poll_default_type: type: string @@ -1603,7 +1551,7 @@ meeting: restriction_mode: B assignment_poll_default_onehundred_percent_base: type: string - enum: *onehundred_percent_bases + enum: *id002 default: valid restriction_mode: B assignment_poll_default_group_ids: @@ -1612,14 +1560,12 @@ meeting: restriction_mode: B assignment_poll_default_backend: type: string - enum: *poll_backends + enum: *id003 default: fast restriction_mode: B - - # Polls poll_ballot_paper_selection: type: string - enum: *ballot_paper_selection + enum: *id001 restriction_mode: B poll_ballot_paper_number: type: number @@ -1636,7 +1582,7 @@ meeting: restriction_mode: B poll_default_onehundred_percent_base: type: string - enum: *onehundred_percent_bases + enum: *id002 default: YNA restriction_mode: B poll_default_group_ids: @@ -1645,26 +1591,23 @@ meeting: restriction_mode: B poll_default_backend: type: string - enum: *poll_backends + enum: *id003 default: fast restriction_mode: B poll_default_live_voting_enabled: type: boolean default: false - description: Defines default 'poll.live_voting_enabled' option suggested to user. Is not used in the validations. + description: Defines default 'poll.live_voting_enabled' option suggested to user. + Is not used in the validations. restriction_mode: B poll_couple_countdown: type: boolean - default: True + default: true restriction_mode: B - - # Topic poll topic_poll_default_group_ids: type: relation-list to: group/used_as_topic_poll_default_id restriction_mode: B - - # Relations projector_ids: type: relation-list to: projector/meeting_id @@ -1848,8 +1791,6 @@ meeting: to: structure_level/meeting_id on_delete: CASCADE restriction_mode: B - - # Logos and Fonts logo_projector_main_id: type: relation to: meeting_mediafile/used_as_logo_projector_main_in_meeting_id @@ -1922,772 +1863,398 @@ meeting: restriction_mode: B font_projector_h1_id: type: relation - to: meeting_mediafile/used_as_font_projector_h1_in_meeting_id - reference: meeting_mediafile - restriction_mode: B - font_projector_h2_id: - type: relation - to: meeting_mediafile/used_as_font_projector_h2_in_meeting_id - reference: meeting_mediafile - restriction_mode: B - # Other relations - committee_id: - type: relation - reference: committee - to: committee/meeting_ids - required: true - restriction_mode: A - constant: true - default_meeting_for_committee_id: - type: relation - to: committee/default_meeting_id - restriction_mode: B - organization_tag_ids: - type: relation-list - to: organization_tag/tagged_ids - restriction_mode: B - present_user_ids: - type: relation-list - to: user/is_present_in_meeting_ids - restriction_mode: B - user_ids: - type: relation-list - description: Calculated. All user ids from all users assigned to groups of this meeting. - read_only: true - sql: | - ( - SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - WHERE mu.meeting_id = m.id - ) AS user_ids - to: user/meeting_ids - restriction_mode: A - reference_projector_id: - type: relation - reference: projector - to: projector/used_as_reference_projector_meeting_id - required: true - restriction_mode: B - list_of_speakers_countdown_id: - type: relation - to: projector_countdown/used_as_list_of_speakers_countdown_meeting_id - reference: projector_countdown - restriction_mode: B - poll_countdown_id: - type: relation - to: projector_countdown/used_as_poll_countdown_meeting_id - reference: projector_countdown - restriction_mode: B - projection_ids: - type: relation-list - to: projection/content_object_id - on_delete: CASCADE - restriction_mode: B - default_projector_agenda_item_list_ids: - type: relation-list - to: projector/used_as_default_projector_for_agenda_item_list_in_meeting_id - restriction_mode: B - required: true - default_projector_topic_ids: - type: relation-list - to: projector/used_as_default_projector_for_topic_in_meeting_id - restriction_mode: B - required: true - default_projector_list_of_speakers_ids: - type: relation-list - to: projector/used_as_default_projector_for_list_of_speakers_in_meeting_id - restriction_mode: B - required: true - default_projector_current_los_ids: - type: relation-list - to: projector/used_as_default_projector_for_current_los_in_meeting_id - restriction_mode: B - required: true - default_projector_motion_ids: - type: relation-list - to: projector/used_as_default_projector_for_motion_in_meeting_id - restriction_mode: B - required: true - default_projector_amendment_ids: - type: relation-list - to: projector/used_as_default_projector_for_amendment_in_meeting_id - restriction_mode: B - required: true - default_projector_motion_block_ids: - type: relation-list - to: projector/used_as_default_projector_for_motion_block_in_meeting_id - restriction_mode: B - required: true - default_projector_assignment_ids: - type: relation-list - to: projector/used_as_default_projector_for_assignment_in_meeting_id - restriction_mode: B - required: true - default_projector_mediafile_ids: - type: relation-list - to: projector/used_as_default_projector_for_mediafile_in_meeting_id - restriction_mode: B - required: true - default_projector_message_ids: - type: relation-list - to: projector/used_as_default_projector_for_message_in_meeting_id - restriction_mode: B - required: true - default_projector_countdown_ids: - type: relation-list - to: projector/used_as_default_projector_for_countdown_in_meeting_id - restriction_mode: B - required: true - default_projector_assignment_poll_ids: - type: relation-list - to: projector/used_as_default_projector_for_assignment_poll_in_meeting_id - restriction_mode: B - required: true - default_projector_motion_poll_ids: - type: relation-list - to: projector/used_as_default_projector_for_motion_poll_in_meeting_id - restriction_mode: B - required: true - default_projector_poll_ids: - type: relation-list - to: projector/used_as_default_projector_for_poll_in_meeting_id - restriction_mode: B - required: true - default_group_id: - type: relation - reference: group - to: group/default_group_for_meeting_id - required: true - restriction_mode: B - admin_group_id: - type: relation - to: group/admin_group_for_meeting_id - reference: group - restriction_mode: A - anonymous_group_id: - type: relation - to: group/anonymous_group_for_meeting_id - reference: group - restriction_mode: B - relevant_history_entry_ids: - type: relation-list - to: history_entry/meeting_id - restriction_mode: A - -structure_level: - id: *id_field - name: - type: string - required: true - restriction_mode: A - color: - type: color - restriction_mode: A - default_time: - type: number - minimum: 0 - restriction_mode: A - meeting_user_ids: - type: relation-list - equal_fields: meeting_id - restriction_mode: A - to: meeting_user/structure_level_ids - structure_level_list_of_speakers_ids: - type: relation-list - equal_fields: meeting_id - restriction_mode: A - to: structure_level_list_of_speakers/structure_level_id - meeting_id: - type: relation - required: true - reference: meeting - to: meeting/structure_level_ids - restriction_mode: A - -group: - id: *id_field - external_id: - type: string - restriction_mode: A - description: unique in meeting - name: - type: string - required: true - restriction_mode: A - permissions: - type: string[] - items: - enum: - - agenda_item.can_manage - - agenda_item.can_see - - agenda_item.can_see_internal - - assignment.can_manage - - assignment.can_manage_polls - - assignment.can_nominate_other - - assignment.can_nominate_self - - assignment.can_see - - chat.can_manage - - list_of_speakers.can_be_speaker - - list_of_speakers.can_manage - - list_of_speakers.can_see - - list_of_speakers.can_manage_moderator_notes - - list_of_speakers.can_see_moderator_notes - - mediafile.can_manage - - mediafile.can_see - - meeting.can_manage_logos_and_fonts - - meeting.can_manage_settings - - meeting.can_see_autopilot - - meeting.can_see_frontpage - - meeting.can_see_history - - meeting.can_see_livestream - - motion.can_create - - motion.can_create_amendments - - motion.can_forward - - motion.can_manage - - motion.can_manage_metadata - - motion.can_manage_polls - - motion.can_see - - motion.can_see_internal - - motion.can_see_origin - - motion.can_support - - poll.can_manage - - poll.can_see_progress - - projector.can_manage - - projector.can_see - - tag.can_manage - - user.can_manage - - user.can_manage_presence - - user.can_see_sensitive_data - - user.can_see - - user.can_update - - user.can_edit_own_delegation - restriction_mode: A - weight: - type: number - restriction_mode: A - - meeting_user_ids: - type: relation-list - to: meeting_user/group_ids - restriction_mode: A - equal_fields: meeting_id - default_group_for_meeting_id: + to: meeting_mediafile/used_as_font_projector_h1_in_meeting_id + reference: meeting_mediafile + restriction_mode: B + font_projector_h2_id: type: relation - to: meeting/default_group_id - on_delete: PROTECT - restriction_mode: A - admin_group_for_meeting_id: + to: meeting_mediafile/used_as_font_projector_h2_in_meeting_id + reference: meeting_mediafile + restriction_mode: B + committee_id: type: relation - to: meeting/admin_group_id - on_delete: PROTECT + reference: committee + to: committee/meeting_ids + required: true restriction_mode: A - anonymous_group_for_meeting_id: + constant: true + default_meeting_for_committee_id: type: relation - to: meeting/anonymous_group_id - on_delete: PROTECT - restriction_mode: A - meeting_mediafile_access_group_ids: + to: committee/default_meeting_id + restriction_mode: B + organization_tag_ids: type: relation-list - to: meeting_mediafile/access_group_ids - equal_fields: meeting_id - restriction_mode: A - meeting_mediafile_inherited_access_group_ids: + to: organization_tag/tagged_ids + restriction_mode: B + present_user_ids: type: relation-list - to: meeting_mediafile/inherited_access_group_ids - description: Calculated field. + to: user/is_present_in_meeting_ids + restriction_mode: B + user_ids: + type: relation-list + description: Calculated. All user ids from all users assigned to groups of this + meeting. read_only: true + sql: "(\n SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id)\n FROM meeting_user_t\ + \ mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id\ + \ = gmu.meeting_user_id\n WHERE mu.meeting_id = m.id\n) AS user_ids\n" + to: user/meeting_ids restriction_mode: A - read_comment_section_ids: + reference_projector_id: + type: relation + reference: projector + to: projector/used_as_reference_projector_meeting_id + required: true + restriction_mode: B + list_of_speakers_countdown_id: + type: relation + to: projector_countdown/used_as_list_of_speakers_countdown_meeting_id + reference: projector_countdown + restriction_mode: B + poll_countdown_id: + type: relation + to: projector_countdown/used_as_poll_countdown_meeting_id + reference: projector_countdown + restriction_mode: B + projection_ids: type: relation-list - to: motion_comment_section/read_group_ids - equal_fields: meeting_id - restriction_mode: A - write_comment_section_ids: + to: projection/content_object_id + on_delete: CASCADE + restriction_mode: B + default_projector_agenda_item_list_ids: type: relation-list - to: motion_comment_section/write_group_ids - equal_fields: meeting_id - restriction_mode: A - read_chat_group_ids: + to: projector/used_as_default_projector_for_agenda_item_list_in_meeting_id + restriction_mode: B + required: true + default_projector_topic_ids: type: relation-list - to: chat_group/read_group_ids - equal_fields: meeting_id - restriction_mode: A - write_chat_group_ids: + to: projector/used_as_default_projector_for_topic_in_meeting_id + restriction_mode: B + required: true + default_projector_list_of_speakers_ids: type: relation-list - to: chat_group/write_group_ids - equal_fields: meeting_id - restriction_mode: A - poll_ids: + to: projector/used_as_default_projector_for_list_of_speakers_in_meeting_id + restriction_mode: B + required: true + default_projector_current_los_ids: type: relation-list - to: poll/entitled_group_ids - equal_fields: meeting_id - restriction_mode: A - used_as_motion_poll_default_id: - type: relation - reference: meeting - to: meeting/motion_poll_default_group_ids - restriction_mode: A - used_as_assignment_poll_default_id: - type: relation - reference: meeting - to: meeting/assignment_poll_default_group_ids - restriction_mode: A - used_as_topic_poll_default_id: - type: relation - reference: meeting - to: meeting/topic_poll_default_group_ids - restriction_mode: A - used_as_poll_default_id: - type: relation - reference: meeting - to: meeting/poll_default_group_ids - restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/group_ids + to: projector/used_as_default_projector_for_current_los_in_meeting_id + restriction_mode: B required: true - restriction_mode: A - constant: true - -personal_note: - id: *id_field - note: - type: HTMLStrict - restriction_mode: A - star: - type: boolean - restriction_mode: A - - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/personal_note_ids - equal_fields: meeting_id - restriction_mode: A + default_projector_motion_ids: + type: relation-list + to: projector/used_as_default_projector_for_motion_in_meeting_id + restriction_mode: B required: true - constant: true - content_object_id: - type: generic-relation - reference: - - motion - to: - collections: - - motion - field: personal_note_ids - equal_fields: meeting_id - restriction_mode: A - constant: true - meeting_id: - type: relation - reference: meeting - to: meeting/personal_note_ids + default_projector_amendment_ids: + type: relation-list + to: projector/used_as_default_projector_for_amendment_in_meeting_id + restriction_mode: B required: true - restriction_mode: A - constant: true - -tag: - id: *id_field - name: - type: string + default_projector_motion_block_ids: + type: relation-list + to: projector/used_as_default_projector_for_motion_block_in_meeting_id + restriction_mode: B required: true - restriction_mode: A - - tagged_ids: - type: generic-relation-list - to: - collections: - - agenda_item - - assignment - - motion - field: tag_ids - equal_fields: meeting_id - restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/tag_ids + default_projector_assignment_ids: + type: relation-list + to: projector/used_as_default_projector_for_assignment_in_meeting_id + restriction_mode: B required: true - restriction_mode: A - constant: true - -agenda_item: - id: *id_field - item_number: - type: string - restriction_mode: A - comment: - type: string - restriction_mode: C - closed: - type: boolean - default: false - restriction_mode: A - type: - type: string - enum: - - common - - internal - - hidden - default: common - restriction_mode: A - duration: - type: number - description: Given in seconds - minimum: 0 + default_projector_mediafile_ids: + type: relation-list + to: projector/used_as_default_projector_for_mediafile_in_meeting_id + restriction_mode: B + required: true + default_projector_message_ids: + type: relation-list + to: projector/used_as_default_projector_for_message_in_meeting_id + restriction_mode: B + required: true + default_projector_countdown_ids: + type: relation-list + to: projector/used_as_default_projector_for_countdown_in_meeting_id + restriction_mode: B + required: true + default_projector_assignment_poll_ids: + type: relation-list + to: projector/used_as_default_projector_for_assignment_poll_in_meeting_id restriction_mode: B - is_internal: - type: boolean - description: Calculated by the server - read_only: true - restriction_mode: A - is_hidden: - type: boolean - description: Calculated by the server - read_only: true - restriction_mode: A - level: - type: number - description: Calculated by the server - read_only: true - restriction_mode: A - weight: - type: number - restriction_mode: A - - content_object_id: - type: generic-relation - reference: - - motion - - motion_block - - assignment - - topic - to: - collections: - - motion - - motion_block - - assignment - - topic - field: agenda_item_id required: true - equal_fields: meeting_id - restriction_mode: A - constant: true - parent_id: + default_projector_motion_poll_ids: + type: relation-list + to: projector/used_as_default_projector_for_motion_poll_in_meeting_id + restriction_mode: B + required: true + default_projector_poll_ids: + type: relation-list + to: projector/used_as_default_projector_for_poll_in_meeting_id + restriction_mode: B + required: true + default_group_id: type: relation - reference: agenda_item - to: agenda_item/child_ids - equal_fields: meeting_id + reference: group + to: group/default_group_for_meeting_id + required: true + restriction_mode: B + admin_group_id: + type: relation + to: group/admin_group_for_meeting_id + reference: group restriction_mode: A - child_ids: + anonymous_group_id: + type: relation + to: group/anonymous_group_for_meeting_id + reference: group + restriction_mode: B + relevant_history_entry_ids: type: relation-list - to: agenda_item/parent_id - equal_fields: meeting_id + to: history_entry/meeting_id restriction_mode: A - tag_ids: - type: relation-list - to: tag/tagged_ids - equal_fields: meeting_id +meeting_mediafile: + id: + type: number restriction_mode: A - projection_ids: - type: relation-list - to: projection/content_object_id - equal_fields: meeting_id - on_delete: CASCADE + constant: true + required: true + mediafile_id: + type: relation + to: mediafile/meeting_mediafile_ids + reference: mediafile + required: true restriction_mode: A meeting_id: type: relation + to: meeting/meeting_mediafile_ids reference: meeting - to: meeting/agenda_item_ids required: true restriction_mode: A - constant: true - -list_of_speakers: - id: *id_field - closed: + is_public: type: boolean - default: false - restriction_mode: A - sequential_number: - type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true - required: true - restriction_mode: A - constant: true - moderator_notes: - type: HTMLStrict - restriction_mode: B - - content_object_id: - type: generic-relation - reference: - - motion - - motion_block - - assignment - - topic - - meeting_mediafile - to: - collections: - - motion - - motion_block - - assignment - - topic - - meeting_mediafile - field: list_of_speakers_id + description: 'Calculated in actions. Used to discern whether the (meeting-)mediafile + can be seen by everyone, because, in the case of inherited_access_group_ids + == [], it would otherwise not be clear. inherited_access_group_ids == [] can + have two causes: cancelling access groups (=> is_public := false) or no access + groups at all (=> is_public := true)' required: true - equal_fields: meeting_id restriction_mode: A - constant: true - speaker_ids: + inherited_access_group_ids: type: relation-list - to: speaker/list_of_speakers_id - on_delete: CASCADE - equal_fields: meeting_id + to: group/meeting_mediafile_inherited_access_group_ids + description: Calculated in actions. Shows what access group permissions are actually + relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids + and the related mediafiles potential parent mediafiles inherited_access_group_ids. + If the parent has no meeting_mediafile for this meeting, its inherited access + group is assumed to be the meetings admin group. If there is no parent, the + inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids + are empty, the interpretations is that every group has access rights, therefore + the parent inherited_access_group_ids are used as-is. restriction_mode: A - structure_level_list_of_speakers_ids: + access_group_ids: type: relation-list + to: group/meeting_mediafile_access_group_ids restriction_mode: A - equal_fields: meeting_id - to: structure_level_list_of_speakers/list_of_speakers_id + list_of_speakers_id: + type: relation + to: list_of_speakers/content_object_id on_delete: CASCADE + restriction_mode: A projection_ids: type: relation-list to: projection/content_object_id - equal_fields: meeting_id on_delete: CASCADE restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/list_of_speakers_ids - required: true + attachment_ids: + type: generic-relation-list + to: + collections: + - motion + - topic + - assignment + field: attachment_meeting_mediafile_ids restriction_mode: A - constant: true - -structure_level_list_of_speakers: - id: *id_field - structure_level_id: + used_as_logo_projector_main_in_meeting_id: type: relation - required: true - equal_fields: meeting_id + to: meeting/logo_projector_main_id restriction_mode: A - reference: structure_level - to: structure_level/structure_level_list_of_speakers_ids - list_of_speakers_id: + used_as_logo_projector_header_in_meeting_id: type: relation - required: true - equal_fields: meeting_id - restriction_mode: A - reference: list_of_speakers - to: list_of_speakers/structure_level_list_of_speakers_ids - speaker_ids: - type: relation-list - equal_fields: meeting_id + to: meeting/logo_projector_header_id restriction_mode: A - to: speaker/structure_level_list_of_speakers_id - initial_time: - type: number - minimum: 1 - required: true + used_as_logo_web_header_in_meeting_id: + type: relation + to: meeting/logo_web_header_id restriction_mode: A - description: The initial time of this structure_level for this LoS - additional_time: - type: float + used_as_logo_pdf_header_l_in_meeting_id: + type: relation + to: meeting/logo_pdf_header_l_id restriction_mode: A - description: The summed added time of this structure_level for this LoS - remaining_time: - type: float - required: true + used_as_logo_pdf_header_r_in_meeting_id: + type: relation + to: meeting/logo_pdf_header_r_id restriction_mode: A - description: The currently remaining time of this structure_level for this LoS - current_start_time: - type: timestamp + used_as_logo_pdf_footer_l_in_meeting_id: + type: relation + to: meeting/logo_pdf_footer_l_id restriction_mode: A - description: The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists - meeting_id: + used_as_logo_pdf_footer_r_in_meeting_id: type: relation - reference: meeting - to: meeting/structure_level_list_of_speakers_ids - required: true + to: meeting/logo_pdf_footer_r_id restriction_mode: A - -point_of_order_category: - id: *id_field - text: - type: string - required: true + used_as_logo_pdf_ballot_paper_in_meeting_id: + type: relation + to: meeting/logo_pdf_ballot_paper_id restriction_mode: A - rank: - type: number - required: true + used_as_font_regular_in_meeting_id: + type: relation + to: meeting/font_regular_id restriction_mode: A - meeting_id: + used_as_font_italic_in_meeting_id: type: relation - reference: meeting - to: meeting/point_of_order_category_ids - required: true + to: meeting/font_italic_id restriction_mode: A - constant: true - speaker_ids: - type: relation-list - to: speaker/point_of_order_category_id - equal_fields: meeting_id + used_as_font_bold_in_meeting_id: + type: relation + to: meeting/font_bold_id restriction_mode: A - -speaker: - id: *id_field - begin_time: - type: timestamp + used_as_font_bold_italic_in_meeting_id: + type: relation + to: meeting/font_bold_italic_id restriction_mode: A - end_time: - type: timestamp + used_as_font_monospace_in_meeting_id: + type: relation + to: meeting/font_monospace_id restriction_mode: A - pause_time: - type: timestamp - read_only: true + used_as_font_chyron_speaker_name_in_meeting_id: + type: relation + to: meeting/font_chyron_speaker_name_id restriction_mode: A - unpause_time: - type: timestamp + used_as_font_projector_h1_in_meeting_id: + type: relation + to: meeting/font_projector_h1_id restriction_mode: A - total_pause: - type: number + used_as_font_projector_h2_in_meeting_id: + type: relation + to: meeting/font_projector_h2_id restriction_mode: A - weight: +meeting_user: + id: type: number - default: 10000 restriction_mode: A - speech_state: + constant: true + required: true + comment: + type: HTMLStrict + restriction_mode: D + number: type: string - enum: - - contribution - - pro - - contra - - intervention - - interposed_question restriction_mode: A - answer: - type: boolean + about_me: + type: HTMLStrict restriction_mode: A - note: - type: string - maxLength: 250 + vote_weight: + type: decimal(6) + minimum: '0.000001' restriction_mode: A - point_of_order: + locked_out: type: boolean - restriction_mode: A + restriction_mode: E + user_id: + type: relation + reference: user + to: user/meeting_user_ids + required: true + restriction_mode: C constant: true - - list_of_speakers_id: + meeting_id: type: relation - reference: list_of_speakers - to: list_of_speakers/speaker_ids + reference: meeting + to: meeting/meeting_user_ids required: true - equal_fields: meeting_id - restriction_mode: A + restriction_mode: C constant: true - structure_level_list_of_speakers_id: - type: relation + personal_note_ids: + type: relation-list + to: personal_note/meeting_user_id equal_fields: meeting_id - restriction_mode: A - reference: structure_level_list_of_speakers - to: structure_level_list_of_speakers/speaker_ids - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/speaker_ids + on_delete: CASCADE + restriction_mode: B + speaker_ids: + type: relation-list + to: speaker/meeting_user_id equal_fields: meeting_id restriction_mode: A - point_of_order_category_id: - type: relation - reference: point_of_order_category - to: point_of_order_category/speaker_ids + motion_supporter_ids: + type: relation-list + to: motion_supporter/meeting_user_id equal_fields: meeting_id restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/speaker_ids - required: true - restriction_mode: A - constant: true - -topic: - id: *id_field - title: - type: string - required: true + motion_editor_ids: + type: relation-list + to: motion_editor/meeting_user_id + equal_fields: meeting_id restriction_mode: A - text: - type: HTMLPermissive + motion_working_group_speaker_ids: + type: relation-list + to: motion_working_group_speaker/meeting_user_id + equal_fields: meeting_id restriction_mode: A - sequential_number: - type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true - required: true + motion_submitter_ids: + type: relation-list + to: motion_submitter/meeting_user_id + equal_fields: meeting_id restriction_mode: A - constant: true - - attachment_meeting_mediafile_ids: + assignment_candidate_ids: type: relation-list - to: meeting_mediafile/attachment_ids + to: assignment_candidate/meeting_user_id equal_fields: meeting_id restriction_mode: A - agenda_item_id: + vote_delegated_to_id: type: relation - to: agenda_item/content_object_id - required: true - on_delete: CASCADE + reference: meeting_user + to: meeting_user/vote_delegations_from_ids equal_fields: meeting_id restriction_mode: A - constant: true - list_of_speakers_id: - type: relation - to: list_of_speakers/content_object_id - required: true - on_delete: CASCADE + vote_delegations_from_ids: + type: relation-list + to: meeting_user/vote_delegated_to_id equal_fields: meeting_id restriction_mode: A - constant: true - poll_ids: + chat_message_ids: type: relation-list - to: poll/content_object_id - on_delete: CASCADE + to: chat_message/meeting_user_id equal_fields: meeting_id restriction_mode: A - projection_ids: + group_ids: type: relation-list - to: projection/content_object_id + to: group/meeting_user_ids equal_fields: meeting_id - on_delete: CASCADE restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/topic_ids - required: true + structure_level_ids: + type: relation-list + equal_fields: meeting_id restriction_mode: A - constant: true - + to: structure_level/meeting_user_ids motion: - id: *id_field + id: + type: number + restriction_mode: A + constant: true + required: true number: type: string restriction_mode: C number_value: type: number - description: The number value of this motion. This number is auto-generated and read-only. + description: The number value of this motion. This number is auto-generated and + read-only. read_only: true restriction_mode: D sequential_number: type: number sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. read_only: true required: true restriction_mode: C @@ -2750,8 +2317,8 @@ motion: marked_forwarded: type: boolean restriction_mode: A - description: Forwarded amendments can be marked as such. This is just optional, however. Forwarded amendments can also have this field set to false. - + description: Forwarded amendments can be marked as such. This is just optional, + however. Forwarded amendments can also have this field set to false. lead_motion_id: type: relation reference: motion @@ -2778,22 +2345,22 @@ motion: origin_id: type: relation reference: motion - to: motion/derived_motion_ids # Note: The related motions may not be in the same meeting + to: motion/derived_motion_ids restriction_mode: A - origin_meeting_id: # The meeting of the motion in origin_id, if any + origin_meeting_id: type: relation reference: meeting to: meeting/forwarded_motion_ids restriction_mode: A derived_motion_ids: type: relation-list - to: motion/origin_id # Note: The related motions may not be in the same meeting + to: motion/origin_id restriction_mode: A - all_origin_ids: # Calculated: All parents (origin_id), grandparents, etc. of this motion. + all_origin_ids: type: relation-list to: motion/all_derived_motion_ids restriction_mode: A - all_derived_motion_ids: # Calculated: All children (derived_motion_ids), grandchildren, etc. of this motion. + all_derived_motion_ids: type: relation-list to: motion/all_origin_ids restriction_mode: A @@ -2818,7 +2385,7 @@ motion: type: generic-relation-list to: collections: - - motion + - motion field: referenced_in_motion_state_extension_ids equal_fields: meeting_id restriction_mode: C @@ -2831,7 +2398,7 @@ motion: type: generic-relation-list to: collections: - - motion + - motion field: referenced_in_motion_recommendation_extension_ids equal_fields: meeting_id restriction_mode: C @@ -2935,72 +2502,269 @@ motion: type: relation-list to: personal_note/content_object_id equal_fields: meeting_id - on_delete: CASCADE - restriction_mode: C + on_delete: CASCADE + restriction_mode: C + meeting_id: + type: relation + reference: meeting + to: meeting/motion_ids + required: true + restriction_mode: A + constant: true + history_entry_ids: + type: relation-list + to: history_entry/model_id + restriction_mode: A +motion_block: + id: + type: number + restriction_mode: A + constant: true + required: true + title: + type: string + required: true + restriction_mode: A + internal: + type: boolean + restriction_mode: A + sequential_number: + type: number + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true + required: true + restriction_mode: A + constant: true + motion_ids: + type: relation-list + to: motion/block_id + equal_fields: meeting_id + restriction_mode: A + agenda_item_id: + type: relation + to: agenda_item/content_object_id + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + list_of_speakers_id: + type: relation + to: list_of_speakers/content_object_id + required: true + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + projection_ids: + type: relation-list + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE + restriction_mode: A + meeting_id: + type: relation + reference: meeting + to: meeting/motion_block_ids + required: true + restriction_mode: A + constant: true +motion_category: + id: + type: number + restriction_mode: A + constant: true + required: true + name: + type: string + required: true + restriction_mode: A + prefix: + type: string + restriction_mode: A + weight: + type: number + default: 10000 + restriction_mode: A + level: + type: number + description: Calculated field. + read_only: true + restriction_mode: A + sequential_number: + type: number + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true + required: true + restriction_mode: A + constant: true + parent_id: + type: relation + reference: motion_category + to: motion_category/child_ids + equal_fields: meeting_id + restriction_mode: A + child_ids: + type: relation-list + to: motion_category/parent_id + equal_fields: meeting_id + restriction_mode: A + motion_ids: + type: relation-list + to: motion/category_id + equal_fields: meeting_id + restriction_mode: A + meeting_id: + type: relation + reference: meeting + to: meeting/motion_category_ids + required: true + restriction_mode: A + constant: true +motion_change_recommendation: + id: + type: number + restriction_mode: A + constant: true + required: true + rejected: + type: boolean + default: false + restriction_mode: A + internal: + type: boolean + default: false + restriction_mode: A + type: + type: string + enum: + - replacement + - insertion + - deletion + - other + default: replacement + restriction_mode: A + other_description: + type: string + restriction_mode: A + line_from: + type: number + minimum: 0 + restriction_mode: A + line_to: + type: number + minimum: 0 + restriction_mode: A + text: + type: HTMLStrict + restriction_mode: A + creation_time: + type: timestamp + read_only: true + restriction_mode: A + motion_id: + type: relation + reference: motion + to: motion/change_recommendation_ids + required: true + equal_fields: meeting_id + restriction_mode: A + constant: true meeting_id: type: relation reference: meeting - to: meeting/motion_ids + to: meeting/motion_change_recommendation_ids required: true restriction_mode: A constant: true - - history_entry_ids: - type: relation-list - to: history_entry/model_id - restriction_mode: A - -motion_submitter: - id: *id_field - weight: +motion_comment: + id: type: number restriction_mode: A - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/motion_submitter_ids + constant: true + required: true + comment: + type: HTMLStrict restriction_mode: A motion_id: type: relation reference: motion - to: motion/submitter_ids + to: motion/comment_ids + required: true equal_fields: meeting_id restriction_mode: A + constant: true + section_id: + type: relation + reference: motion_comment_section + to: motion_comment_section/comment_ids required: true + equal_fields: meeting_id + restriction_mode: A constant: true meeting_id: type: relation reference: meeting - to: meeting/motion_submitter_ids + to: meeting/motion_comment_ids required: true restriction_mode: A constant: true - -motion_supporter: - id: *id_field - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/motion_supporter_ids +motion_comment_section: + id: + type: number restriction_mode: A - motion_id: - type: relation - reference: motion - to: motion/supporter_ids - equal_fields: meeting_id + constant: true + required: true + name: + type: string + required: true + restriction_mode: A + weight: + type: number + default: 10000 restriction_mode: A + sequential_number: + type: number + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true required: true + restriction_mode: A constant: true + submitter_can_write: + type: boolean + restriction_mode: A + comment_ids: + type: relation-list + to: motion_comment/section_id + on_delete: PROTECT + equal_fields: meeting_id + restriction_mode: A + read_group_ids: + type: relation-list + to: group/read_comment_section_ids + equal_fields: meeting_id + restriction_mode: A + write_group_ids: + type: relation-list + to: group/write_comment_section_ids + equal_fields: meeting_id + restriction_mode: A meeting_id: type: relation reference: meeting - to: meeting/motion_supporter_ids + to: meeting/motion_comment_section_ids required: true restriction_mode: A constant: true - motion_editor: - id: *id_field + id: + type: number + restriction_mode: A + constant: true + required: true weight: type: number restriction_mode: A @@ -3024,456 +2788,615 @@ motion_editor: required: true restriction_mode: A constant: true - -motion_working_group_speaker: - id: *id_field +motion_state: + id: + type: number + restriction_mode: A + constant: true + required: true + name: + type: string + required: true + restriction_mode: A weight: type: number + required: true restriction_mode: A - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/motion_working_group_speaker_ids + recommendation_label: + type: string restriction_mode: A - motion_id: + is_internal: + type: boolean + restriction_mode: A + css_class: + type: string + enum: + - grey + - red + - green + - lightblue + - yellow + default: lightblue + required: true + restriction_mode: A + restrictions: + type: string[] + items: + enum: + - motion.can_see_internal + - motion.can_manage_metadata + - motion.can_manage + - is_submitter + default: [] + restriction_mode: A + allow_support: + type: boolean + default: false + restriction_mode: A + allow_create_poll: + type: boolean + default: false + restriction_mode: A + allow_submitter_edit: + type: boolean + default: false + restriction_mode: A + set_number: + type: boolean + default: true + restriction_mode: A + show_state_extension_field: + type: boolean + default: false + restriction_mode: A + show_recommendation_extension_field: + type: boolean + default: false + restriction_mode: A + merge_amendment_into_final: + type: string + enum: + - do_not_merge + - undefined + - do_merge + default: undefined + restriction_mode: A + allow_motion_forwarding: + type: boolean + restriction_mode: A + default: false + allow_amendment_forwarding: + type: boolean + restriction_mode: A + set_workflow_timestamp: + type: boolean + restriction_mode: A + default: false + state_button_label: + type: string + restriction_mode: A + submitter_withdraw_state_id: type: relation - reference: motion - to: motion/working_group_speaker_ids + reference: motion_state + to: motion_state/submitter_withdraw_back_ids + equal_fields: + - meeting_id + - workflow_id + restriction_mode: A + submitter_withdraw_back_ids: + type: relation-list + to: motion_state/submitter_withdraw_state_id + equal_fields: + - meeting_id + - workflow_id + restriction_mode: A + next_state_ids: + type: relation-list + to: motion_state/previous_state_ids + equal_fields: + - meeting_id + - workflow_id + restriction_mode: A + previous_state_ids: + type: relation-list + to: motion_state/next_state_ids + equal_fields: + - meeting_id + - workflow_id + restriction_mode: A + motion_ids: + type: relation-list + to: motion/state_id + on_delete: PROTECT equal_fields: meeting_id restriction_mode: A + motion_recommendation_ids: + type: relation-list + to: motion/recommendation_id + equal_fields: meeting_id + restriction_mode: A + workflow_id: + type: relation + reference: motion_workflow + to: motion_workflow/state_ids required: true - constant: true + equal_fields: meeting_id + restriction_mode: A + first_state_of_workflow_id: + type: relation + to: motion_workflow/first_state_id + on_delete: PROTECT + equal_fields: meeting_id + restriction_mode: A meeting_id: type: relation reference: meeting - to: meeting/motion_working_group_speaker_ids + to: meeting/motion_state_ids required: true restriction_mode: A constant: true - -motion_comment: - id: *id_field - comment: - type: HTMLStrict + deferred: true +motion_submitter: + id: + type: number restriction_mode: A - - motion_id: - type: relation - reference: motion - to: motion/comment_ids + constant: true required: true - equal_fields: meeting_id + weight: + type: number restriction_mode: A - constant: true - section_id: + meeting_user_id: type: relation - reference: motion_comment_section - to: motion_comment_section/comment_ids - required: true + reference: meeting_user + to: meeting_user/motion_submitter_ids + restriction_mode: A + motion_id: + type: relation + reference: motion + to: motion/submitter_ids equal_fields: meeting_id restriction_mode: A + required: true constant: true meeting_id: type: relation reference: meeting - to: meeting/motion_comment_ids + to: meeting/motion_submitter_ids required: true restriction_mode: A constant: true - -motion_comment_section: - id: *id_field - name: - type: string - required: true - restriction_mode: A - weight: - type: number - default: 10000 - restriction_mode: A - sequential_number: +motion_supporter: + id: type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true - required: true restriction_mode: A constant: true - submitter_can_write: - type: boolean - restriction_mode: A - - comment_ids: - type: relation-list - to: motion_comment/section_id - on_delete: PROTECT - equal_fields: meeting_id - restriction_mode: A - read_group_ids: - type: relation-list - to: group/read_comment_section_ids - equal_fields: meeting_id + required: true + meeting_user_id: + type: relation + reference: meeting_user + to: meeting_user/motion_supporter_ids restriction_mode: A - write_group_ids: - type: relation-list - to: group/write_comment_section_ids + motion_id: + type: relation + reference: motion + to: motion/supporter_ids equal_fields: meeting_id restriction_mode: A + required: true + constant: true meeting_id: type: relation reference: meeting - to: meeting/motion_comment_section_ids + to: meeting/motion_supporter_ids required: true restriction_mode: A constant: true - -motion_category: - id: *id_field +motion_workflow: + id: + type: number + restriction_mode: A + constant: true + required: true name: type: string required: true restriction_mode: A - prefix: - type: string - restriction_mode: A - weight: - type: number - default: 10000 - restriction_mode: A - level: - type: number - description: Calculated field. - read_only: true - restriction_mode: A sequential_number: type: number sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. read_only: true required: true restriction_mode: A constant: true - - parent_id: - type: relation - reference: motion_category - to: motion_category/child_ids - equal_fields: meeting_id - restriction_mode: A - child_ids: + state_ids: type: relation-list - to: motion_category/parent_id + to: motion_state/workflow_id + on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - motion_ids: - type: relation-list - to: motion/category_id + first_state_id: + type: relation + reference: motion_state + to: motion_state/first_state_of_workflow_id + required: true equal_fields: meeting_id restriction_mode: A + default_workflow_meeting_id: + type: relation + to: meeting/motions_default_workflow_id + restriction_mode: A + default_amendment_workflow_meeting_id: + type: relation + to: meeting/motions_default_amendment_workflow_id + restriction_mode: A meeting_id: type: relation reference: meeting - to: meeting/motion_category_ids + to: meeting/motion_workflow_ids required: true restriction_mode: A constant: true - -motion_block: - id: *id_field - title: - type: string - required: true - restriction_mode: A - internal: - type: boolean - restriction_mode: A - sequential_number: +motion_working_group_speaker: + id: type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true - required: true restriction_mode: A constant: true - - motion_ids: - type: relation-list - to: motion/block_id - equal_fields: meeting_id + required: true + weight: + type: number restriction_mode: A - agenda_item_id: + meeting_user_id: type: relation - to: agenda_item/content_object_id - on_delete: CASCADE - equal_fields: meeting_id + reference: meeting_user + to: meeting_user/motion_working_group_speaker_ids restriction_mode: A - list_of_speakers_id: + motion_id: type: relation - to: list_of_speakers/content_object_id - required: true - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - projection_ids: - type: relation-list - to: projection/content_object_id + reference: motion + to: motion/working_group_speaker_ids equal_fields: meeting_id - on_delete: CASCADE restriction_mode: A + required: true + constant: true meeting_id: type: relation reference: meeting - to: meeting/motion_block_ids + to: meeting/motion_working_group_speaker_ids required: true restriction_mode: A constant: true - -motion_change_recommendation: - id: *id_field - rejected: - type: boolean - default: false - restriction_mode: A - internal: - type: boolean - default: false - restriction_mode: A - type: - type: string - enum: - - replacement - - insertion - - deletion - - other - default: replacement - restriction_mode: A - other_description: - type: string - restriction_mode: A - line_from: +option: + id: type: number - minimum: 0 restriction_mode: A - line_to: + constant: true + required: true + weight: type: number - minimum: 0 + default: 10000 restriction_mode: A text: type: HTMLStrict restriction_mode: A - creation_time: - type: timestamp - read_only: true + 'yes': + type: decimal(6) + restriction_mode: B + 'no': + type: decimal(6) + restriction_mode: B + abstain: + type: decimal(6) + restriction_mode: B + poll_id: + type: relation + to: poll/option_ids + reference: poll + equal_fields: meeting_id restriction_mode: A - - motion_id: + constant: true + used_as_global_option_in_poll_id: type: relation - reference: motion - to: motion/change_recommendation_ids - required: true + to: poll/global_option_id + reference: poll + equal_fields: meeting_id + restriction_mode: A + constant: true + vote_ids: + type: relation-list + to: vote/option_id + reference: vote + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + content_object_id: + type: generic-relation + to: + - motion/option_ids + - user/option_ids + - poll_candidate_list/option_id + reference: + - motion + - user + - poll_candidate_list equal_fields: meeting_id restriction_mode: A constant: true meeting_id: type: relation + to: meeting/option_ids reference: meeting - to: meeting/motion_change_recommendation_ids required: true restriction_mode: A constant: true - -motion_state: - id: *id_field - name: - type: string - required: true - restriction_mode: A - weight: +organization: + id: type: number - required: true restriction_mode: A - recommendation_label: + constant: true + required: true + name: type: string restriction_mode: A - is_internal: - type: boolean - restriction_mode: A - css_class: - type: string - enum: - - grey - - red - - green - - lightblue - - yellow - default: lightblue - required: true + description: + type: HTMLStrict restriction_mode: A - restrictions: - type: string[] - items: - enum: - - motion.can_see_internal - - motion.can_manage_metadata - - motion.can_manage - - is_submitter - default: [] + legal_notice: + type: text restriction_mode: A - allow_support: - type: boolean - default: false + privacy_policy: + type: text restriction_mode: A - allow_create_poll: - type: boolean - default: false + login_text: + type: text restriction_mode: A - allow_submitter_edit: + reset_password_verbose_errors: type: boolean - default: false + restriction_mode: B + gender_ids: + type: relation-list + to: gender/organization_id + reference: gender restriction_mode: A - set_number: + disable_forward_with_attachments: type: boolean - default: true restriction_mode: A - show_state_extension_field: + enable_electronic_voting: type: boolean - default: false - restriction_mode: A - show_recommendation_extension_field: + restriction_mode: B + enable_chat: type: boolean - default: false restriction_mode: A - merge_amendment_into_final: + limit_of_meetings: + type: number + description: Maximum of active meetings for the whole organization. 0 means no + limitation at all + restriction_mode: C + default: 0 + minimum: 0 + limit_of_users: + type: number + description: Maximum of active users for the whole organization. 0 means no limitation + at all + restriction_mode: C + default: 0 + minimum: 0 + default_language: type: string - enum: - - do_not_merge - - undefined - - do_merge - default: undefined restriction_mode: A - allow_motion_forwarding: + enum: + - en + - de + - it + - es + - ru + - cs + - fr + default: en + require_duplicate_from: type: boolean restriction_mode: A - default: false - allow_amendment_forwarding: + enable_anonymous: type: boolean restriction_mode: A - set_workflow_timestamp: + saml_enabled: type: boolean restriction_mode: A - default: false - state_button_label: + saml_login_button_text: type: string + default: SAML login restriction_mode: A - submitter_withdraw_state_id: - type: relation - reference: motion_state - to: motion_state/submitter_withdraw_back_ids - equal_fields: - - meeting_id - - workflow_id + saml_attr_mapping: + type: JSON + restriction_mode: D + saml_metadata_idp: + type: text + restriction_mode: D + saml_metadata_sp: + type: text + restriction_mode: D + saml_private_key: + type: text + restriction_mode: D + committee_ids: + type: relation-list + to: committee/organization_id + restriction_mode: B + reference: committee + active_meeting_ids: + type: relation-list restriction_mode: A - submitter_withdraw_back_ids: + to: meeting/is_active_in_organization_id + archived_meeting_ids: type: relation-list - to: motion_state/submitter_withdraw_state_id - equal_fields: - - meeting_id - - workflow_id + restriction_mode: B + to: meeting/is_archived_in_organization_id + template_meeting_ids: + type: relation-list + to: meeting/template_for_organization_id restriction_mode: A - - next_state_ids: + organization_tag_ids: type: relation-list - to: motion_state/previous_state_ids - equal_fields: - - meeting_id - - workflow_id restriction_mode: A - previous_state_ids: + to: organization_tag/organization_id + reference: organization_tag + theme_id: + type: relation + required: true + restriction_mode: A + to: theme/theme_for_organization_id + reference: theme + theme_ids: type: relation-list - to: motion_state/next_state_ids - equal_fields: - - meeting_id - - workflow_id restriction_mode: A - motion_ids: + to: theme/organization_id + reference: theme + mediafile_ids: type: relation-list - to: motion/state_id - on_delete: PROTECT - equal_fields: meeting_id + to: mediafile/owner_id + on_delete: CASCADE restriction_mode: A - motion_recommendation_ids: + published_mediafile_ids: type: relation-list - to: motion/recommendation_id - equal_fields: meeting_id + to: mediafile/published_to_meetings_in_organization_id + restriction_mode: E + user_ids: + type: relation-list + restriction_mode: C + to: user/organization_id + reference: user + users_email_sender: + type: string + default: OpenSlides restriction_mode: A - workflow_id: - type: relation - reference: motion_workflow - to: motion_workflow/state_ids - required: true - equal_fields: meeting_id + users_email_replyto: + type: string restriction_mode: A - first_state_of_workflow_id: - type: relation - to: motion_workflow/first_state_id - on_delete: PROTECT - equal_fields: meeting_id + users_email_subject: + type: string + default: OpenSlides access data restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/motion_state_ids - required: true + users_email_body: + type: text + default: 'Dear {name}, + + + this is your personal OpenSlides login: + + + {url} + + Username: {username} + + Password: {password} + + + + This email was generated automatically.' + restriction_mode: A + url: + type: string + default: https://example.com + restriction_mode: A +organization_tag: + id: + type: number restriction_mode: A constant: true - deferred: true - -motion_workflow: - id: *id_field + required: true name: type: string required: true restriction_mode: A - sequential_number: - type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true + color: + type: color + required: true + restriction_mode: A + tagged_ids: + type: generic-relation-list + to: + collections: + - committee + - meeting + field: organization_tag_ids + restriction_mode: A + organization_id: + type: relation + reference: organization + to: organization/organization_tag_ids + restriction_mode: A required: true +personal_note: + id: + type: number restriction_mode: A constant: true - - state_ids: - type: relation-list - to: motion_state/workflow_id - on_delete: CASCADE - equal_fields: meeting_id + required: true + note: + type: HTMLStrict restriction_mode: A - first_state_id: + star: + type: boolean + restriction_mode: A + meeting_user_id: type: relation - reference: motion_state - to: motion_state/first_state_of_workflow_id + reference: meeting_user + to: meeting_user/personal_note_ids + equal_fields: meeting_id + restriction_mode: A required: true + constant: true + content_object_id: + type: generic-relation + reference: + - motion + to: + collections: + - motion + field: personal_note_ids equal_fields: meeting_id restriction_mode: A - default_workflow_meeting_id: + constant: true + meeting_id: type: relation - to: meeting/motions_default_workflow_id + reference: meeting + to: meeting/personal_note_ids + required: true restriction_mode: A - default_amendment_workflow_meeting_id: - type: relation - to: meeting/motions_default_amendment_workflow_id + constant: true +point_of_order_category: + id: + type: number + restriction_mode: A + constant: true + required: true + text: + type: string + required: true + restriction_mode: A + rank: + type: number + required: true restriction_mode: A meeting_id: type: relation reference: meeting - to: meeting/motion_workflow_ids + to: meeting/point_of_order_category_ids required: true restriction_mode: A constant: true - + speaker_ids: + type: relation-list + to: speaker/point_of_order_category_id + equal_fields: meeting_id + restriction_mode: A poll: - id: *id_field + id: + type: number + restriction_mode: A + constant: true + required: true title: type: string required: true @@ -3485,15 +3408,17 @@ poll: type: string required: true enum: - - analog - - named - - pseudoanonymous - - cryptographic + - analog + - named + - pseudoanonymous + - cryptographic restriction_mode: A backend: type: string - required: True - enum: *poll_backends + required: true + enum: + - long + - fast default: fast restriction_mode: A is_pseudoanonymized: @@ -3503,18 +3428,18 @@ poll: type: string required: true enum: - - "Y" - - "YN" - - "YNA" - - "N" + - Y + - YN + - YNA + - N restriction_mode: A state: type: string enum: - - created - - started - - finished - - published + - created + - started + - finished + - published default: created restriction_mode: A min_votes_amount: @@ -3547,7 +3472,16 @@ poll: onehundred_percent_base: type: string required: true - enum: *onehundred_percent_bases + enum: + - Y + - YN + - YNA + - N + - valid + - cast + - entitled + - entitled_present + - disabled default: disabled restriction_mode: A votesvalid: @@ -3565,17 +3499,20 @@ poll: live_voting_enabled: type: boolean default: false - description: If true, the vote service sends the votes of the users to the autoupdate service. + description: If true, the vote service sends the votes of the users to the autoupdate + service. restriction_mode: A live_votes: type: JSON calculated: true - description: dict from user to their vote. The value is null, when live voting is disabled. + description: dict from user to their vote. The value is null, when live voting + is disabled. restriction_mode: C sequential_number: type: number sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. read_only: true required: true restriction_mode: A @@ -3583,14 +3520,14 @@ poll: content_object_id: type: generic-relation reference: + - motion + - assignment + - topic + to: + collections: - motion - assignment - topic - to: - collections: - - motion - - assignment - - topic field: poll_ids equal_fields: meeting_id restriction_mode: A @@ -3633,981 +3570,1027 @@ poll: restriction_mode: A required: true constant: true - -option: - id: *id_field - weight: +poll_candidate: + id: type: number - default: 10000 - restriction_mode: A - text: - type: HTMLStrict restriction_mode: A - "yes": - type: decimal(6) - restriction_mode: B - "no": - type: decimal(6) - restriction_mode: B - abstain: - type: decimal(6) - restriction_mode: B - - poll_id: + constant: true + required: true + poll_candidate_list_id: type: relation - to: poll/option_ids - reference: poll + to: poll_candidate_list/poll_candidate_ids + reference: poll_candidate_list equal_fields: meeting_id restriction_mode: A + required: true constant: true - used_as_global_option_in_poll_id: + user_id: type: relation - to: poll/global_option_id - reference: poll - equal_fields: meeting_id + to: user/poll_candidate_ids + reference: user restriction_mode: A constant: true - vote_ids: - type: relation-list - to: vote/option_id - reference: vote - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - content_object_id: - type: generic-relation - to: - - motion/option_ids - - user/option_ids - - poll_candidate_list/option_id - reference: - - motion - - user - - poll_candidate_list - equal_fields: meeting_id + weight: + type: number + required: true restriction_mode: A - constant: true meeting_id: type: relation - to: meeting/option_ids + to: meeting/poll_candidate_ids reference: meeting required: true restriction_mode: A constant: true - -vote: - id: *id_field - weight: - type: decimal(6) +poll_candidate_list: + id: + type: number restriction_mode: A constant: true - value: - type: string + required: true + poll_candidate_ids: + type: relation-list + to: poll_candidate/poll_candidate_list_id + reference: poll_candidate + on_delete: CASCADE restriction_mode: A - constant: true - user_token: - type: string + equal_fields: meeting_id + meeting_id: + type: relation + to: meeting/poll_candidate_list_ids + reference: meeting required: true - restriction_mode: B + restriction_mode: A constant: true - option_id: type: relation - to: option/vote_ids - reference: option + to: option/content_object_id equal_fields: meeting_id required: true restriction_mode: A constant: true - user_id: +projection: + id: + type: number + restriction_mode: A + constant: true + required: true + options: + type: JSON + restriction_mode: A + stable: + type: boolean + default: false + restriction_mode: A + weight: + type: number + restriction_mode: A + type: + type: string + restriction_mode: A + content: + type: JSON + calculated: true + restriction_mode: A + current_projector_id: type: relation - to: user/vote_ids - reference: user + reference: projector + to: projector/current_projection_ids + equal_fields: meeting_id restriction_mode: A - delegated_user_id: + preview_projector_id: type: relation - to: user/delegated_vote_ids - reference: user + reference: projector + to: projector/preview_projection_ids + equal_fields: meeting_id + restriction_mode: A + history_projector_id: + type: relation + reference: projector + to: projector/history_projection_ids + equal_fields: meeting_id + restriction_mode: A + content_object_id: + type: generic-relation + reference: + - meeting + - motion + - meeting_mediafile + - list_of_speakers + - motion_block + - assignment + - agenda_item + - topic + - poll + - projector_message + - projector_countdown + to: + - meeting/projection_ids + - motion/projection_ids + - meeting_mediafile/projection_ids + - list_of_speakers/projection_ids + - motion_block/projection_ids + - assignment/projection_ids + - agenda_item/projection_ids + - topic/projection_ids + - poll/projection_ids + - projector_message/projection_ids + - projector_countdown/projection_ids + equal_fields: meeting_id restriction_mode: A + required: true + constant: true meeting_id: type: relation - to: meeting/vote_ids reference: meeting + to: meeting/all_projection_ids required: true restriction_mode: A constant: true - -assignment: - id: *id_field - title: - type: string +projector: + id: + type: number + restriction_mode: A + constant: true required: true + name: + type: string restriction_mode: A - description: - type: HTMLStrict + is_internal: + type: boolean + default: false restriction_mode: A - open_posts: + scale: type: number - minimum: 0 default: 0 restriction_mode: A - phase: - type: string - enum: - - search - - voting - - finished - default: search + scroll: + type: number + default: 0 + minimum: 0 restriction_mode: A - default_poll_description: - type: text + width: + type: number + minimum: 1 + default: 1200 restriction_mode: A - number_poll_candidates: + aspect_ratio_numerator: + type: number + minimum: 1 + default: 16 + restriction_mode: A + aspect_ratio_denominator: + type: number + minimum: 1 + default: 9 + restriction_mode: A + color: + type: color + default: '#000000' + restriction_mode: A + background_color: + type: color + default: '#ffffff' + restriction_mode: A + header_background_color: + type: color + default: '#317796' + restriction_mode: A + header_font_color: + type: color + default: '#f5f5f5' + restriction_mode: A + header_h1_color: + type: color + default: '#317796' + restriction_mode: A + chyron_background_color: + type: color + default: '#317796' + restriction_mode: A + chyron_background_color_2: + type: color + default: '#134768' + restriction_mode: A + chyron_font_color: + type: color + default: '#ffffff' + restriction_mode: A + chyron_font_color_2: + type: color + default: '#ffffff' + restriction_mode: A + show_header_footer: + type: boolean + default: true + restriction_mode: A + show_title: + type: boolean + default: true + restriction_mode: A + show_logo: + type: boolean + default: true + restriction_mode: A + show_clock: type: boolean + default: true restriction_mode: A sequential_number: type: number sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. read_only: true required: true restriction_mode: A constant: true - - candidate_ids: + current_projection_ids: type: relation-list - to: assignment_candidate/assignment_id + to: projection/current_projector_id on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - poll_ids: + preview_projection_ids: type: relation-list - to: poll/content_object_id + to: projection/preview_projector_id on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - agenda_item_id: - type: relation - to: agenda_item/content_object_id + history_projection_ids: + type: relation-list + to: projection/history_projector_id on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - list_of_speakers_id: + used_as_reference_projector_meeting_id: type: relation - to: list_of_speakers/content_object_id - required: true - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - constant: true - tag_ids: - type: relation-list - to: tag/tagged_ids - equal_fields: meeting_id + to: meeting/reference_projector_id restriction_mode: A - attachment_meeting_mediafile_ids: - type: relation-list - to: meeting_mediafile/attachment_ids - equal_fields: meeting_id + used_as_default_projector_for_agenda_item_list_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_agenda_item_list_ids restriction_mode: A - projection_ids: - type: relation-list - to: projection/content_object_id - equal_fields: meeting_id - on_delete: CASCADE + used_as_default_projector_for_topic_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_topic_ids restriction_mode: A - meeting_id: + used_as_default_projector_for_list_of_speakers_in_meeting_id: type: relation reference: meeting - to: meeting/assignment_ids - required: true + to: meeting/default_projector_list_of_speakers_ids restriction_mode: A - constant: true - - history_entry_ids: - type: relation-list - to: history_entry/model_id + used_as_default_projector_for_current_los_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_current_los_ids restriction_mode: A - -assignment_candidate: - id: *id_field - weight: - type: number - default: 10000 + used_as_default_projector_for_motion_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_motion_ids restriction_mode: A - - assignment_id: + used_as_default_projector_for_amendment_in_meeting_id: type: relation - reference: assignment - to: assignment/candidate_ids - equal_fields: meeting_id + reference: meeting + to: meeting/default_projector_amendment_ids restriction_mode: A - required: true - constant: true - meeting_user_id: + used_as_default_projector_for_motion_block_in_meeting_id: type: relation - reference: meeting_user - to: meeting_user/assignment_candidate_ids + reference: meeting + to: meeting/default_projector_motion_block_ids restriction_mode: A - constant: true - meeting_id: + used_as_default_projector_for_assignment_in_meeting_id: type: relation reference: meeting - to: meeting/assignment_candidate_ids - required: true + to: meeting/default_projector_assignment_ids restriction_mode: A - constant: true - -# models for the poll list election -poll_candidate_list: - id: *id_field - poll_candidate_ids: - type: relation-list - to: poll_candidate/poll_candidate_list_id - reference: poll_candidate - on_delete: CASCADE + used_as_default_projector_for_mediafile_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_mediafile_ids restriction_mode: A - equal_fields: meeting_id - meeting_id: + used_as_default_projector_for_message_in_meeting_id: type: relation - to: meeting/poll_candidate_list_ids reference: meeting - required: true + to: meeting/default_projector_message_ids restriction_mode: A - constant: true - option_id: + used_as_default_projector_for_countdown_in_meeting_id: type: relation - to: option/content_object_id - equal_fields: meeting_id - required: true + reference: meeting + to: meeting/default_projector_countdown_ids restriction_mode: A - constant: true - -poll_candidate: - id: *id_field - poll_candidate_list_id: + used_as_default_projector_for_assignment_poll_in_meeting_id: type: relation - to: poll_candidate_list/poll_candidate_ids - reference: poll_candidate_list - equal_fields: meeting_id + reference: meeting + to: meeting/default_projector_assignment_poll_ids restriction_mode: A - required: true - constant: true - user_id: + used_as_default_projector_for_motion_poll_in_meeting_id: type: relation - to: user/poll_candidate_ids - reference: user + reference: meeting + to: meeting/default_projector_motion_poll_ids restriction_mode: A - constant: true - weight: - type: number - required: true + used_as_default_projector_for_poll_in_meeting_id: + type: relation + reference: meeting + to: meeting/default_projector_poll_ids restriction_mode: A meeting_id: type: relation - to: meeting/poll_candidate_ids reference: meeting + to: meeting/projector_ids + restriction_mode: A required: true + constant: true +projector_countdown: + id: + type: number restriction_mode: A constant: true - -# Mediafiles are delivered by the mediafile server with the URL -# `/media//path` -mediafile: - id: *id_field + required: true title: type: string - description: Title and parent_id must be unique. - restriction_mode: A - is_directory: - type: boolean - restriction_mode: A - filesize: - type: number - description: In bytes, not the human readable format anymore. - read_only: true + required: true restriction_mode: A - filename: + description: type: string - description: The uploaded filename. Will be used for downloading. Only writeable on create. + default: '' restriction_mode: A - mimetype: - type: string + default_time: + type: number restriction_mode: A - pdf_information: - type: JSON + countdown_time: + type: float + default: 60 restriction_mode: A - create_timestamp: - type: timestamp + running: + type: boolean + default: false restriction_mode: A - token: - type: string + projection_ids: + type: relation-list + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE restriction_mode: A - published_to_meetings_in_organization_id: + used_as_list_of_speakers_countdown_meeting_id: type: relation - to: organization/published_mediafile_ids - reference: organization + to: meeting/list_of_speakers_countdown_id restriction_mode: A - parent_id: + used_as_poll_countdown_meeting_id: type: relation - reference: mediafile - to: mediafile/child_ids - equal_fields: owner_id - restriction_mode: A - child_ids: - type: relation-list - to: mediafile/parent_id - equal_fields: owner_id + to: meeting/poll_countdown_id restriction_mode: A - owner_id: - type: generic-relation - to: - - meeting/mediafile_ids - - organization/mediafile_ids - reference: - - meeting - - organization + meeting_id: + type: relation + reference: meeting + to: meeting/projector_countdown_ids restriction_mode: A required: true constant: true - meeting_mediafile_ids: - type: relation-list - to: meeting_mediafile/mediafile_id - on_delete: CASCADE +projector_message: + id: + type: number restriction_mode: A - -meeting_mediafile: - id: *id_field - mediafile_id: - type: relation - to: mediafile/meeting_mediafile_ids - reference: mediafile + constant: true required: true + message: + type: HTMLStrict + restriction_mode: A + projection_ids: + type: relation-list + to: projection/content_object_id + equal_fields: meeting_id + on_delete: CASCADE restriction_mode: A meeting_id: type: relation - to: meeting/meeting_mediafile_ids reference: meeting + to: meeting/projector_message_ids + restriction_mode: A required: true + constant: true +speaker: + id: + type: number restriction_mode: A - is_public: - type: boolean - description: "Calculated in actions. Used to discern whether the (meeting-)mediafile can be seen by everyone, because, in the case of inherited_access_group_ids == [], it would otherwise not be clear. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)" + constant: true required: true + begin_time: + type: timestamp restriction_mode: A - inherited_access_group_ids: - type: relation-list - to: group/meeting_mediafile_inherited_access_group_ids - description: Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is. + end_time: + type: timestamp restriction_mode: A - access_group_ids: - type: relation-list - to: group/meeting_mediafile_access_group_ids + pause_time: + type: timestamp + read_only: true restriction_mode: A - list_of_speakers_id: - type: relation - to: list_of_speakers/content_object_id - on_delete: CASCADE + unpause_time: + type: timestamp restriction_mode: A - projection_ids: - type: relation-list - to: projection/content_object_id - on_delete: CASCADE + total_pause: + type: number restriction_mode: A - attachment_ids: - type: generic-relation-list - to: - collections: - - motion - - topic - - assignment - field: attachment_meeting_mediafile_ids + weight: + type: number + default: 10000 restriction_mode: A - - # Reverse relations for meetings, if a mediafile is used as a special resource - used_as_logo_projector_main_in_meeting_id: - type: relation - to: meeting/logo_projector_main_id + speech_state: + type: string + enum: + - contribution + - pro + - contra + - intervention + - interposed_question + restriction_mode: A + answer: + type: boolean + restriction_mode: A + note: + type: string + maxLength: 250 + restriction_mode: A + point_of_order: + type: boolean restriction_mode: A - used_as_logo_projector_header_in_meeting_id: + constant: true + list_of_speakers_id: type: relation - to: meeting/logo_projector_header_id + reference: list_of_speakers + to: list_of_speakers/speaker_ids + required: true + equal_fields: meeting_id restriction_mode: A - used_as_logo_web_header_in_meeting_id: + constant: true + structure_level_list_of_speakers_id: type: relation - to: meeting/logo_web_header_id + equal_fields: meeting_id restriction_mode: A - used_as_logo_pdf_header_l_in_meeting_id: + reference: structure_level_list_of_speakers + to: structure_level_list_of_speakers/speaker_ids + meeting_user_id: type: relation - to: meeting/logo_pdf_header_l_id + reference: meeting_user + to: meeting_user/speaker_ids + equal_fields: meeting_id restriction_mode: A - used_as_logo_pdf_header_r_in_meeting_id: + point_of_order_category_id: type: relation - to: meeting/logo_pdf_header_r_id + reference: point_of_order_category + to: point_of_order_category/speaker_ids + equal_fields: meeting_id restriction_mode: A - used_as_logo_pdf_footer_l_in_meeting_id: + meeting_id: type: relation - to: meeting/logo_pdf_footer_l_id + reference: meeting + to: meeting/speaker_ids + required: true restriction_mode: A - used_as_logo_pdf_footer_r_in_meeting_id: - type: relation - to: meeting/logo_pdf_footer_r_id + constant: true +structure_level: + id: + type: number restriction_mode: A - used_as_logo_pdf_ballot_paper_in_meeting_id: - type: relation - to: meeting/logo_pdf_ballot_paper_id + constant: true + required: true + name: + type: string + required: true restriction_mode: A - used_as_font_regular_in_meeting_id: - type: relation - to: meeting/font_regular_id + color: + type: color restriction_mode: A - used_as_font_italic_in_meeting_id: - type: relation - to: meeting/font_italic_id + default_time: + type: number + minimum: 0 restriction_mode: A - used_as_font_bold_in_meeting_id: - type: relation - to: meeting/font_bold_id + meeting_user_ids: + type: relation-list + equal_fields: meeting_id restriction_mode: A - used_as_font_bold_italic_in_meeting_id: - type: relation - to: meeting/font_bold_italic_id + to: meeting_user/structure_level_ids + structure_level_list_of_speakers_ids: + type: relation-list + equal_fields: meeting_id restriction_mode: A - used_as_font_monospace_in_meeting_id: + to: structure_level_list_of_speakers/structure_level_id + meeting_id: type: relation - to: meeting/font_monospace_id + required: true + reference: meeting + to: meeting/structure_level_ids restriction_mode: A - used_as_font_chyron_speaker_name_in_meeting_id: +structure_level_list_of_speakers: + id: + type: number + restriction_mode: A + constant: true + required: true + structure_level_id: type: relation - to: meeting/font_chyron_speaker_name_id + required: true + equal_fields: meeting_id restriction_mode: A - used_as_font_projector_h1_in_meeting_id: + reference: structure_level + to: structure_level/structure_level_list_of_speakers_ids + list_of_speakers_id: type: relation - to: meeting/font_projector_h1_id + required: true + equal_fields: meeting_id restriction_mode: A - used_as_font_projector_h2_in_meeting_id: + reference: list_of_speakers + to: list_of_speakers/structure_level_list_of_speakers_ids + speaker_ids: + type: relation-list + equal_fields: meeting_id + restriction_mode: A + to: speaker/structure_level_list_of_speakers_id + initial_time: + type: number + minimum: 1 + required: true + restriction_mode: A + description: The initial time of this structure_level for this LoS + additional_time: + type: float + restriction_mode: A + description: The summed added time of this structure_level for this LoS + remaining_time: + type: float + required: true + restriction_mode: A + description: The currently remaining time of this structure_level for this LoS + current_start_time: + type: timestamp + restriction_mode: A + description: The current start time of a speaker for this structure_level. Is + only set if a currently speaking speaker exists + meeting_id: type: relation - to: meeting/font_projector_h2_id + reference: meeting + to: meeting/structure_level_list_of_speakers_ids + required: true restriction_mode: A - -projector: - id: *id_field +tag: + id: + type: number + restriction_mode: A + constant: true + required: true name: type: string + required: true restriction_mode: A - is_internal: - type: boolean - default: false - restriction_mode: A - scale: - type: number - default: 0 + tagged_ids: + type: generic-relation-list + to: + collections: + - agenda_item + - assignment + - motion + field: tag_ids + equal_fields: meeting_id restriction_mode: A - scroll: - type: number - default: 0 - minimum: 0 + meeting_id: + type: relation + reference: meeting + to: meeting/tag_ids + required: true restriction_mode: A - width: + constant: true +theme: + id: type: number - minimum: 1 - default: 1200 restriction_mode: A - aspect_ratio_numerator: - type: number - minimum: 1 - default: 16 + constant: true + required: true + name: restriction_mode: A - aspect_ratio_denominator: - type: number - minimum: 1 - default: 9 + type: string + required: true + accent_100: restriction_mode: A - color: type: color - default: "#000000" + accent_200: restriction_mode: A - background_color: type: color - default: "#ffffff" + accent_300: restriction_mode: A - header_background_color: type: color - default: "#317796" + accent_400: restriction_mode: A - header_font_color: type: color - default: "#f5f5f5" + accent_50: restriction_mode: A - header_h1_color: type: color - default: "#317796" + accent_500: restriction_mode: A - chyron_background_color: type: color - default: "#317796" + default: '#2196f3' + accent_600: restriction_mode: A - chyron_background_color_2: type: color - default: "#134768" + accent_700: restriction_mode: A - chyron_font_color: type: color - default: "#ffffff" + accent_800: restriction_mode: A - chyron_font_color_2: type: color - default: "#ffffff" - restriction_mode: A - show_header_footer: - type: boolean - default: true - restriction_mode: A - show_title: - type: boolean - default: true - restriction_mode: A - show_logo: - type: boolean - default: true - restriction_mode: A - show_clock: - type: boolean - default: true - restriction_mode: A - sequential_number: - type: number - sequence_scope: meeting_id - description: The (positive) serial number of this model in its meeting. This number is auto-generated and read-only. - read_only: true - required: true - restriction_mode: A - constant: true - - current_projection_ids: - type: relation-list - to: projection/current_projector_id - on_delete: CASCADE - equal_fields: meeting_id - restriction_mode: A - preview_projection_ids: - type: relation-list - to: projection/preview_projector_id - on_delete: CASCADE - equal_fields: meeting_id + accent_900: restriction_mode: A - history_projection_ids: - type: relation-list - to: projection/history_projector_id - on_delete: CASCADE - equal_fields: meeting_id + type: color + accent_a100: restriction_mode: A - used_as_reference_projector_meeting_id: - type: relation - to: meeting/reference_projector_id + type: color + accent_a200: restriction_mode: A - used_as_default_projector_for_agenda_item_list_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_agenda_item_list_ids + type: color + accent_a400: restriction_mode: A - used_as_default_projector_for_topic_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_topic_ids + type: color + accent_a700: restriction_mode: A - used_as_default_projector_for_list_of_speakers_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_list_of_speakers_ids + type: color + primary_100: restriction_mode: A - used_as_default_projector_for_current_los_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_current_los_ids + type: color + primary_200: restriction_mode: A - used_as_default_projector_for_motion_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_motion_ids + type: color + primary_300: restriction_mode: A - used_as_default_projector_for_amendment_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_amendment_ids + type: color + primary_400: restriction_mode: A - used_as_default_projector_for_motion_block_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_motion_block_ids + type: color + primary_50: restriction_mode: A - used_as_default_projector_for_assignment_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_assignment_ids + type: color + primary_500: restriction_mode: A - used_as_default_projector_for_mediafile_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_mediafile_ids + type: color + default: '#317796' + primary_600: restriction_mode: A - used_as_default_projector_for_message_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_message_ids + type: color + primary_700: restriction_mode: A - used_as_default_projector_for_countdown_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_countdown_ids + type: color + primary_800: restriction_mode: A - used_as_default_projector_for_assignment_poll_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_assignment_poll_ids + type: color + primary_900: restriction_mode: A - used_as_default_projector_for_motion_poll_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_motion_poll_ids + type: color + primary_a100: restriction_mode: A - used_as_default_projector_for_poll_in_meeting_id: - type: relation - reference: meeting - to: meeting/default_projector_poll_ids + type: color + primary_a200: restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/projector_ids + type: color + primary_a400: restriction_mode: A - required: true - constant: true - -# A projection is an M2M model between a projector and an element which is assigned to it. -# It can either be the current projection, a preview or a history projection (i.e. previously -# projected, with reference retained for protocolling purposes). The projection can only be -# connected to the projector once in one of these contexts. Projections are projector- and -# element-specific, meaning that, while the type of projector reference may change, neither -# the referenced projector, nor the content_object of the projection may be changed. -projection: - id: *id_field - options: - type: JSON + type: color + primary_a700: restriction_mode: A - stable: - type: boolean - default: false + type: color + warn_100: restriction_mode: A - weight: - type: number + type: color + warn_200: restriction_mode: A - type: - type: string + type: color + warn_300: restriction_mode: A - - content: - type: JSON - calculated: true + type: color + warn_400: restriction_mode: A - - current_projector_id: - type: relation - reference: projector - to: projector/current_projection_ids - equal_fields: meeting_id + type: color + warn_50: restriction_mode: A - preview_projector_id: - type: relation - reference: projector - to: projector/preview_projection_ids - equal_fields: meeting_id + type: color + warn_500: restriction_mode: A - history_projector_id: - type: relation - reference: projector - to: projector/history_projection_ids - equal_fields: meeting_id + type: color + default: '#f06400' + warn_600: restriction_mode: A - content_object_id: - type: generic-relation - reference: - - meeting - - motion - - meeting_mediafile - - list_of_speakers - - motion_block - - assignment - - agenda_item - - topic - - poll - - projector_message - - projector_countdown - to: - - meeting/projection_ids - - motion/projection_ids - - meeting_mediafile/projection_ids - - list_of_speakers/projection_ids - - motion_block/projection_ids - - assignment/projection_ids - - agenda_item/projection_ids - - topic/projection_ids - - poll/projection_ids - - projector_message/projection_ids - - projector_countdown/projection_ids - equal_fields: meeting_id + type: color + warn_700: restriction_mode: A - required: true - constant: true - meeting_id: - type: relation - reference: meeting - to: meeting/all_projection_ids - required: true + type: color + warn_800: restriction_mode: A - constant: true - -projector_message: - id: *id_field - message: - type: HTMLStrict + type: color + warn_900: restriction_mode: A - - projection_ids: - type: relation-list - to: projection/content_object_id - equal_fields: meeting_id - on_delete: CASCADE + type: color + warn_a100: restriction_mode: A - meeting_id: - type: relation - reference: meeting - to: meeting/projector_message_ids + type: color + warn_a200: restriction_mode: A - required: true - constant: true - -projector_countdown: - id: *id_field - title: - type: string - required: true + type: color + warn_a400: restriction_mode: A - description: - type: string - default: "" + type: color + warn_a700: restriction_mode: A - default_time: - type: number + type: color + headbar: restriction_mode: A - countdown_time: - type: float - default: 60 + type: color + 'yes': restriction_mode: A - running: - type: boolean - default: false + type: color + 'no': restriction_mode: A - - projection_ids: - type: relation-list - to: projection/content_object_id - equal_fields: meeting_id - on_delete: CASCADE + type: color + abstain: restriction_mode: A - used_as_list_of_speakers_countdown_meeting_id: - type: relation - to: meeting/list_of_speakers_countdown_id + type: color + theme_for_organization_id: restriction_mode: A - used_as_poll_countdown_meeting_id: + to: organization/theme_id type: relation - to: meeting/poll_countdown_id - restriction_mode: A - meeting_id: + organization_id: type: relation - reference: meeting - to: meeting/projector_countdown_ids + reference: organization + to: organization/theme_ids restriction_mode: A required: true +topic: + id: + type: number + restriction_mode: A constant: true - -chat_group: - id: *id_field - name: + required: true + title: type: string required: true restriction_mode: A - weight: + text: + type: HTMLPermissive + restriction_mode: A + sequential_number: type: number - default: 10000 + sequence_scope: meeting_id + description: The (positive) serial number of this model in its meeting. This number + is auto-generated and read-only. + read_only: true + required: true restriction_mode: A - chat_message_ids: + constant: true + attachment_meeting_mediafile_ids: type: relation-list - to: chat_message/chat_group_id + to: meeting_mediafile/attachment_ids equal_fields: meeting_id restriction_mode: A + agenda_item_id: + type: relation + to: agenda_item/content_object_id + required: true on_delete: CASCADE - read_group_ids: + equal_fields: meeting_id + restriction_mode: A + constant: true + list_of_speakers_id: + type: relation + to: list_of_speakers/content_object_id + required: true + on_delete: CASCADE + equal_fields: meeting_id + restriction_mode: A + constant: true + poll_ids: type: relation-list - to: group/read_chat_group_ids + to: poll/content_object_id + on_delete: CASCADE equal_fields: meeting_id restriction_mode: A - write_group_ids: + projection_ids: type: relation-list - to: group/write_chat_group_ids + to: projection/content_object_id equal_fields: meeting_id + on_delete: CASCADE restriction_mode: A meeting_id: type: relation reference: meeting - to: meeting/chat_group_ids + to: meeting/topic_ids required: true restriction_mode: A constant: true - -chat_message: - id: *id_field - content: - type: HTMLStrict - required: true - restriction_mode: A - created: - type: timestamp - required: true - restriction_mode: A - meeting_user_id: - type: relation - reference: meeting_user - to: meeting_user/chat_message_ids +user: + id: + type: number restriction_mode: A constant: true - chat_group_id: - type: relation - reference: chat_group - to: chat_group/chat_message_ids - restriction_mode: A required: true - constant: true - meeting_id: - type: relation - reference: meeting - to: meeting/chat_message_ids + username: + type: string required: true + restriction_mode: B + member_number: + type: string + restriction_mode: B + saml_id: + type: string + minLength: 1 + restriction_mode: B + description: unique-key from IdP for SAML login + pronoun: + type: string + maxLength: 32 restriction_mode: A - constant: true - -action_worker: - id: *id_field - name: + title: type: string - required: true restriction_mode: A - state: + first_name: type: string - required: true - enum: - - running - - end - - aborted restriction_mode: A - created: - type: timestamp - required: true + last_name: + type: string restriction_mode: A - timestamp: + is_active: + type: boolean + default: true + restriction_mode: B + is_physical_person: + type: boolean + default: true + restriction_mode: A + password: + type: string + restriction_mode: G + default_password: + type: string + restriction_mode: H + can_change_own_password: + type: boolean + default: true + restriction_mode: D + email: + type: string + restriction_mode: B + default_vote_weight: + type: decimal(6) + default: '1.000000' + minimum: '0.000001' + restriction_mode: A + last_email_sent: type: timestamp - required: true + restriction_mode: B + is_demo_user: + type: boolean restriction_mode: A - result: - type: JSON + last_login: + type: timestamp restriction_mode: A - user_id: - type: number - description: "Id of the calling user. If the action is called via internal route, the value will be -1." - required: true + read_only: true + external: + type: boolean + restriction_mode: E + gender_id: + type: relation + to: gender/user_ids restriction_mode: A - constant: true - -import_preview: - id: *id_field - name: + reference: gender + organization_management_level: type: string - required: true + description: Hierarchical permission level for the whole organization. enum: - - account - - participant - - topic - - committee - - motion + - superadmin + - can_manage_organization + - can_manage_users + restriction_mode: B + is_present_in_meeting_ids: + type: relation-list + to: meeting/present_user_ids restriction_mode: A - state: - type: string - required: true - enum: - - warning - - error - - done + committee_ids: + type: relation-list + to: committee/user_ids + restriction_mode: E + read_only: true + description: 'Calculated field: Returns committee_ids, where the user is manager + or member in a meeting' + sql: "(\n SELECT array_agg(DISTINCT committee_id ORDER BY committee_id)\n FROM\ + \ (\n -- Select committee_ids from meetings the user is part of\n SELECT\ + \ m.committee_id\n FROM meeting_user_t AS mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t\ + \ AS gmu ON mu.id = gmu.meeting_user_id\n INNER JOIN meeting_t AS m ON m.id\ + \ = mu.meeting_id\n WHERE mu.user_id = u.id\n\n UNION\n\n -- Select\ + \ committee_ids from committee managers\n SELECT cmu.committee_id\n FROM\ + \ nm_committee_manager_ids_user_t cmu\n WHERE cmu.user_id = u.id\n\n UNION\n\ + \n -- Select home_committee_id from user\n SELECT u.home_committee_id\n\ + \ WHERE u.home_committee_id IS NOT NULL\n ) _\n) AS committee_ids\n" + committee_management_ids: + type: relation-list + to: committee/manager_ids + restriction_mode: E + meeting_user_ids: + type: relation-list + to: meeting_user/user_id restriction_mode: A - created: - type: timestamp - required: true + on_delete: CASCADE + poll_voted_ids: + type: relation-list + to: poll/voted_ids restriction_mode: A - result: - type: JSON + option_ids: + type: relation-list + to: option/content_object_id + reference: option restriction_mode: A - -history_position: - id: *id_field - timestamp: - type: timestamp + vote_ids: + type: relation-list + to: vote/user_id + reference: vote restriction_mode: A - read_only: true - original_user_id: - type: number + delegated_vote_ids: + type: relation-list + to: vote/delegated_user_id + reference: vote restriction_mode: A - constant: true - user_id: + poll_candidate_ids: + type: relation-list + to: poll_candidate/user_id + reference: poll_candidate + restriction_mode: A + home_committee_id: type: relation - to: user/history_position_ids + to: committee/native_user_ids + restriction_mode: E + reference: committee + history_position_ids: + type: relation-list + to: history_position/user_id restriction_mode: A - reference: user - entry_ids: + history_entry_ids: type: relation-list - to: history_entry/position_id + to: history_entry/model_id restriction_mode: A - on_delete: CASCADE - -history_entry: - id: *id_field - entries: - type: text[] + meeting_ids: + type: relation-list + description: Calculated. All ids from meetings calculated via meeting_user and + group_ids as integers. + read_only: true + sql: "(\n SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id)\n \ + \ FROM meeting_user_t mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t\ + \ AS gmu ON mu.id = gmu.meeting_user_id\n WHERE mu.user_id = u.id\n) AS meeting_ids\n" + to: meeting/user_ids + restriction_mode: E + organization_id: + type: relation + reference: organization + to: organization/user_ids + required: true + restriction_mode: F + constant: true +vote: + id: + type: number restriction_mode: A - original_model_id: - type: string + constant: true + required: true + weight: + type: decimal(6) restriction_mode: A constant: true - model_id: - type: generic-relation - to: - collections: - - user - - motion - - assignment - field: history_entry_ids + value: + type: string restriction_mode: A - reference: - - user - - motion - - assignment - position_id: + constant: true + user_token: + type: string + required: true + restriction_mode: B + constant: true + option_id: type: relation - to: history_position/entry_ids + to: option/vote_ids + reference: option + equal_fields: meeting_id required: true restriction_mode: A constant: true - reference: history_position - meeting_id: + user_id: type: relation - to: meeting/relevant_history_entry_ids + to: user/vote_ids + reference: user + restriction_mode: A + delegated_user_id: + type: relation + to: user/delegated_vote_ids + reference: user restriction_mode: A + meeting_id: + type: relation + to: meeting/vote_ids reference: meeting + required: true + restriction_mode: A + constant: true From b97fe64eb790fe1f69578cc3fd97c510a76e9b9e Mon Sep 17 00:00:00 2001 From: Hannes Janott Date: Thu, 22 Jan 2026 16:24:52 +0100 Subject: [PATCH 129/142] [rel-db] Add version table (#333) * add version table with migration_state, and replace tables per migration_index * add table locking via trigger * remove id column from gm tables * support generation of migration table names * centralize usage of MODELS in InternalHelper * extend meeting name length from 100 to 200 --- collections/meeting.yml | 2 +- dev/sql/schema_relational.sql | 3219 ++++++++++++++++---------------- dev/src/generate_sql_schema.py | 34 +- dev/src/helper_get_names.py | 25 +- models.yml | 2 +- 5 files changed, 1654 insertions(+), 1628 deletions(-) diff --git a/collections/meeting.yml b/collections/meeting.yml index 3079dba5..eba23580 100644 --- a/collections/meeting.yml +++ b/collections/meeting.yml @@ -20,7 +20,7 @@ welcome_text: # General name: type: string - maxLength: 100 + maxLength: 200 default: OpenSlides restriction_mode: A required: true diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 7a50c3c6..beb45de9 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'cf9d9816258d1c3187d3dbc536233389' +-- MODELS_YML_CHECKSUM = '2ab51b5ebc2fc37f2202a887952b52a2' -- Function and meta table definitions @@ -56,7 +56,7 @@ BEGIN operation_var := LOWER(TG_OP); fqid_var := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - fqid_var = escaped_table_name || '/' || OLD.id; + fqid_var := escaped_table_name || '/' || OLD.id; END IF; INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) @@ -77,7 +77,7 @@ DECLARE value_1 integer; value_2 integer; BEGIN - base_column_name = TG_ARGV[0]; + base_column_name := TG_ARGV[0]; value_1 := hstore(NEW) -> (base_column_name || '_1'); value_2 := hstore(NEW) -> (base_column_name || '_2'); @@ -95,7 +95,7 @@ DECLARE payload TEXT; body_content_text TEXT; BEGIN - -- Running the trigger for the first time in a transaction creates the table and after commiting the transaction the table is dropped. + -- Running the trigger for the first time in a transaction creates the table and after committing the transaction the table is dropped. -- Every next run of the trigger in this transaction raises a notice that the table exists. Setting the log_min_messages to notice increases the noise because of such messages. CREATE LOCAL TEMPORARY TABLE IF NOT EXISTS tbl_notify_counter_tx_once ( @@ -158,7 +158,19 @@ CREATE TABLE os_notify_log_t ( CONSTRAINT unique_fqid_xact_id_operation UNIQUE (operation,fqid,xact_id) ); -CREATE FUNCTION check_not_null_for_1_1() RETURNS trigger as $not_null_trigger$ +CREATE TABLE version ( + migration_index INTEGER PRIMARY KEY, + migration_state TEXT, + replace_tables JSONB +); + +CREATE OR REPLACE FUNCTION prevent_writes() RETURNS trigger AS $read_only_trigger$ +BEGIN + RAISE EXCEPTION 'Table % is currently read-only.', TG_TABLE_NAME; +END; +$read_only_trigger$ LANGUAGE plpgsql; + +CREATE FUNCTION check_not_null_for_1_1() RETURNS trigger AS $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name: relation to check, usually a view -- column_name: field to check, usually a field in a view @@ -198,7 +210,7 @@ BEGIN END; $not_null_trigger$ language plpgsql; -CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger as $not_null_trigger$ +CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger AS $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name: relation to check, usually a view -- column_name: field to check, usually a field in a view @@ -244,231 +256,270 @@ $not_null_trigger$ language plpgsql; -- Table definitions -CREATE TABLE organization_t ( +CREATE TABLE action_worker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256), - description text, - legal_notice text, - privacy_policy text, - login_text text, - reset_password_verbose_errors boolean, - disable_forward_with_attachments boolean, - enable_electronic_voting boolean, - enable_chat boolean, - limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, - limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, - default_language varchar(256) CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', - require_duplicate_from boolean, - enable_anonymous boolean, - saml_enabled boolean, - saml_login_button_text varchar(256) DEFAULT 'SAML login', - saml_attr_mapping jsonb, - saml_metadata_idp text, - saml_metadata_sp text, - saml_private_key text, - theme_id integer NOT NULL UNIQUE, - users_email_sender varchar(256) DEFAULT 'OpenSlides', - users_email_replyto varchar(256), - users_email_subject varchar(256) DEFAULT 'OpenSlides access data', - users_email_body text DEFAULT 'Dear {name}, + name varchar(256) NOT NULL, + state varchar(256) NOT NULL CONSTRAINT enum_action_worker_state CHECK (state IN ('running', 'end', 'aborted')), + created timestamptz NOT NULL, + timestamp timestamptz NOT NULL, + result jsonb, + user_id integer NOT NULL +); -this is your personal OpenSlides login: -{url} -Username: {username} -Password: {password} + +comment on column action_worker_t.user_id is 'Id of the calling user. If the action is called via internal route, the value will be -1.'; -This email was generated automatically.', - url varchar(256) DEFAULT 'https://example.com' +CREATE TABLE agenda_item_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + item_number varchar(256), + comment varchar(256), + closed boolean DEFAULT False, + type varchar(256) CONSTRAINT enum_agenda_item_type CHECK (type IN ('common', 'internal', 'hidden')) DEFAULT 'common', + duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), + is_internal boolean, + is_hidden boolean, + level integer, + weight integer, + content_object_id varchar(100) NOT NULL, + content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic')), + parent_id integer, + meeting_id integer NOT NULL ); -comment on column organization_t.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; -comment on column organization_t.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; +comment on column agenda_item_t.duration is 'Given in seconds'; +comment on column agenda_item_t.is_internal is 'Calculated by the server'; +comment on column agenda_item_t.is_hidden is 'Calculated by the server'; +comment on column agenda_item_t.level is 'Calculated by the server'; -CREATE TABLE user_t ( +CREATE TABLE assignment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - username varchar(256) NOT NULL, - member_number varchar(256), - saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), - pronoun varchar(32), - title varchar(256), - first_name varchar(256), - last_name varchar(256), - is_active boolean DEFAULT True, - is_physical_person boolean DEFAULT True, - password varchar(256), - default_password varchar(256), - can_change_own_password boolean DEFAULT True, - email varchar(256), - default_vote_weight decimal(16,6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', - last_email_sent timestamptz, - is_demo_user boolean, - last_login timestamptz, - external boolean, - gender_id integer, - organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), - home_committee_id integer, - organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL + title varchar(256) NOT NULL, + description text, + open_posts integer CONSTRAINT minimum_open_posts CHECK (open_posts >= 0) DEFAULT 0, + phase varchar(256) CONSTRAINT enum_assignment_phase CHECK (phase IN ('search', 'voting', 'finished')) DEFAULT 'search', + default_poll_description text, + number_poll_candidates boolean, + sequential_number integer NOT NULL, + CONSTRAINT unique_assignment_sequential_number UNIQUE (sequential_number, meeting_id), + meeting_id integer NOT NULL ); -comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; -comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; +comment on column assignment_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE meeting_user_t ( +CREATE TABLE assignment_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - comment text, - number varchar(256), - about_me text, - vote_weight decimal(16,6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), - locked_out boolean, - user_id integer NOT NULL, - meeting_id integer NOT NULL, - vote_delegated_to_id integer + weight integer DEFAULT 10000, + assignment_id integer NOT NULL, + meeting_user_id integer, + meeting_id integer NOT NULL ); -CREATE TABLE gender_t ( +CREATE TABLE chat_group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL + weight integer DEFAULT 10000, + meeting_id integer NOT NULL ); -comment on column gender_t.name is 'unique'; +CREATE TABLE chat_message_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + content text NOT NULL, + created timestamptz NOT NULL, + meeting_user_id integer, + chat_group_id integer NOT NULL, + meeting_id integer NOT NULL +); -CREATE TABLE organization_tag_t ( + + + +CREATE TABLE committee_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') NOT NULL, + description text, + external_id varchar(256), + default_meeting_id integer UNIQUE, + parent_id integer, organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); +comment on column committee_t.external_id is 'unique'; -CREATE TABLE theme_t ( + +CREATE TABLE gender_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - accent_100 varchar(7) CHECK (accent_100 is null or accent_100 ~* '^#[a-f0-9]{6}$'), - accent_200 varchar(7) CHECK (accent_200 is null or accent_200 ~* '^#[a-f0-9]{6}$'), - accent_300 varchar(7) CHECK (accent_300 is null or accent_300 ~* '^#[a-f0-9]{6}$'), - accent_400 varchar(7) CHECK (accent_400 is null or accent_400 ~* '^#[a-f0-9]{6}$'), - accent_50 varchar(7) CHECK (accent_50 is null or accent_50 ~* '^#[a-f0-9]{6}$'), - accent_500 varchar(7) CHECK (accent_500 is null or accent_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#2196f3', - accent_600 varchar(7) CHECK (accent_600 is null or accent_600 ~* '^#[a-f0-9]{6}$'), - accent_700 varchar(7) CHECK (accent_700 is null or accent_700 ~* '^#[a-f0-9]{6}$'), - accent_800 varchar(7) CHECK (accent_800 is null or accent_800 ~* '^#[a-f0-9]{6}$'), - accent_900 varchar(7) CHECK (accent_900 is null or accent_900 ~* '^#[a-f0-9]{6}$'), - accent_a100 varchar(7) CHECK (accent_a100 is null or accent_a100 ~* '^#[a-f0-9]{6}$'), - accent_a200 varchar(7) CHECK (accent_a200 is null or accent_a200 ~* '^#[a-f0-9]{6}$'), - accent_a400 varchar(7) CHECK (accent_a400 is null or accent_a400 ~* '^#[a-f0-9]{6}$'), - accent_a700 varchar(7) CHECK (accent_a700 is null or accent_a700 ~* '^#[a-f0-9]{6}$'), - primary_100 varchar(7) CHECK (primary_100 is null or primary_100 ~* '^#[a-f0-9]{6}$'), - primary_200 varchar(7) CHECK (primary_200 is null or primary_200 ~* '^#[a-f0-9]{6}$'), - primary_300 varchar(7) CHECK (primary_300 is null or primary_300 ~* '^#[a-f0-9]{6}$'), - primary_400 varchar(7) CHECK (primary_400 is null or primary_400 ~* '^#[a-f0-9]{6}$'), - primary_50 varchar(7) CHECK (primary_50 is null or primary_50 ~* '^#[a-f0-9]{6}$'), - primary_500 varchar(7) CHECK (primary_500 is null or primary_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', - primary_600 varchar(7) CHECK (primary_600 is null or primary_600 ~* '^#[a-f0-9]{6}$'), - primary_700 varchar(7) CHECK (primary_700 is null or primary_700 ~* '^#[a-f0-9]{6}$'), - primary_800 varchar(7) CHECK (primary_800 is null or primary_800 ~* '^#[a-f0-9]{6}$'), - primary_900 varchar(7) CHECK (primary_900 is null or primary_900 ~* '^#[a-f0-9]{6}$'), - primary_a100 varchar(7) CHECK (primary_a100 is null or primary_a100 ~* '^#[a-f0-9]{6}$'), - primary_a200 varchar(7) CHECK (primary_a200 is null or primary_a200 ~* '^#[a-f0-9]{6}$'), - primary_a400 varchar(7) CHECK (primary_a400 is null or primary_a400 ~* '^#[a-f0-9]{6}$'), - primary_a700 varchar(7) CHECK (primary_a700 is null or primary_a700 ~* '^#[a-f0-9]{6}$'), - warn_100 varchar(7) CHECK (warn_100 is null or warn_100 ~* '^#[a-f0-9]{6}$'), - warn_200 varchar(7) CHECK (warn_200 is null or warn_200 ~* '^#[a-f0-9]{6}$'), - warn_300 varchar(7) CHECK (warn_300 is null or warn_300 ~* '^#[a-f0-9]{6}$'), - warn_400 varchar(7) CHECK (warn_400 is null or warn_400 ~* '^#[a-f0-9]{6}$'), - warn_50 varchar(7) CHECK (warn_50 is null or warn_50 ~* '^#[a-f0-9]{6}$'), - warn_500 varchar(7) CHECK (warn_500 is null or warn_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#f06400', - warn_600 varchar(7) CHECK (warn_600 is null or warn_600 ~* '^#[a-f0-9]{6}$'), - warn_700 varchar(7) CHECK (warn_700 is null or warn_700 ~* '^#[a-f0-9]{6}$'), - warn_800 varchar(7) CHECK (warn_800 is null or warn_800 ~* '^#[a-f0-9]{6}$'), - warn_900 varchar(7) CHECK (warn_900 is null or warn_900 ~* '^#[a-f0-9]{6}$'), - warn_a100 varchar(7) CHECK (warn_a100 is null or warn_a100 ~* '^#[a-f0-9]{6}$'), - warn_a200 varchar(7) CHECK (warn_a200 is null or warn_a200 ~* '^#[a-f0-9]{6}$'), - warn_a400 varchar(7) CHECK (warn_a400 is null or warn_a400 ~* '^#[a-f0-9]{6}$'), - warn_a700 varchar(7) CHECK (warn_a700 is null or warn_a700 ~* '^#[a-f0-9]{6}$'), - headbar varchar(7) CHECK (headbar is null or headbar ~* '^#[a-f0-9]{6}$'), - yes varchar(7) CHECK (yes is null or yes ~* '^#[a-f0-9]{6}$'), - no varchar(7) CHECK (no is null or no ~* '^#[a-f0-9]{6}$'), - abstain varchar(7) CHECK (abstain is null or abstain ~* '^#[a-f0-9]{6}$'), organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); +comment on column gender_t.name is 'unique'; -CREATE TABLE committee_t ( + +CREATE TABLE group_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, - description text, external_id varchar(256), - default_meeting_id integer UNIQUE, - parent_id integer, - organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL + name varchar(256) NOT NULL, + permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'assignment.can_manage', 'assignment.can_manage_polls', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'list_of_speakers.can_manage_moderator_notes', 'list_of_speakers.can_see_moderator_notes', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_see_origin', 'motion.can_support', 'poll.can_manage', 'poll.can_see_progress', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update', 'user.can_edit_own_delegation']::varchar[]), + weight integer, + used_as_motion_poll_default_id integer, + used_as_assignment_poll_default_id integer, + used_as_topic_poll_default_id integer, + used_as_poll_default_id integer, + meeting_id integer NOT NULL ); -comment on column committee_t.external_id is 'unique'; +comment on column group_t.external_id is 'unique in meeting'; -CREATE TABLE meeting_t ( +CREATE TABLE history_entry_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - external_id varchar(256), - welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', - welcome_text text DEFAULT 'Space for your welcome text.', - name varchar(100) NOT NULL DEFAULT 'OpenSlides', - is_active_in_organization_id integer, - is_archived_in_organization_id integer, - description varchar(100), - location varchar(256), - start_time timestamptz, - end_time timestamptz, - locked_from_inside boolean, - imported_at timestamptz, - language varchar(256) CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', - jitsi_domain varchar(256), - jitsi_room_name varchar(256), - jitsi_room_password varchar(256), - template_for_organization_id integer, - enable_anonymous boolean DEFAULT False, - custom_translations jsonb, - conference_show boolean DEFAULT False, - conference_auto_connect boolean DEFAULT False, - conference_los_restriction boolean DEFAULT True, - conference_stream_url varchar(256), - conference_stream_poster_url varchar(256), - conference_open_microphone boolean DEFAULT False, - conference_open_video boolean DEFAULT False, - conference_auto_connect_next_speakers integer DEFAULT 0, - conference_enable_helpdesk boolean DEFAULT False, - applause_enable boolean DEFAULT False, - applause_type varchar(256) CONSTRAINT enum_meeting_applause_type CHECK (applause_type IN ('applause-type-bar', 'applause-type-particles')) DEFAULT 'applause-type-bar', - applause_show_level boolean DEFAULT False, - applause_min_amount integer CONSTRAINT minimum_applause_min_amount CHECK (applause_min_amount >= 0) DEFAULT 1, - applause_max_amount integer CONSTRAINT minimum_applause_max_amount CHECK (applause_max_amount >= 0) DEFAULT 0, - applause_timeout integer CONSTRAINT minimum_applause_timeout CHECK (applause_timeout >= 0) DEFAULT 5, - applause_particle_image_url varchar(256), - projector_countdown_default_time integer NOT NULL DEFAULT 60, - projector_countdown_warning_time integer NOT NULL CONSTRAINT minimum_projector_countdown_warning_time CHECK (projector_countdown_warning_time >= 0) DEFAULT 0, - export_csv_encoding varchar(256) CONSTRAINT enum_meeting_export_csv_encoding CHECK (export_csv_encoding IN ('utf-8', 'iso-8859-15')) DEFAULT 'utf-8', - export_csv_separator varchar(256) DEFAULT ';', - export_pdf_pagenumber_alignment varchar(256) CONSTRAINT enum_meeting_export_pdf_pagenumber_alignment CHECK (export_pdf_pagenumber_alignment IN ('left', 'right', 'center')) DEFAULT 'center', + entries text[], + original_model_id varchar(256), + model_id varchar(100), + model_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'user' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + model_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'motion' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + model_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'assignment' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_model_id_part1 CHECK (split_part(model_id, '/', 1) IN ('user','motion','assignment')), + position_id integer NOT NULL, + meeting_id integer +); + + + + +CREATE TABLE history_position_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + timestamp timestamptz, + original_user_id integer, + user_id integer +); + + + + +CREATE TABLE import_preview_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name varchar(256) NOT NULL CONSTRAINT enum_import_preview_name CHECK (name IN ('account', 'participant', 'topic', 'committee', 'motion')), + state varchar(256) NOT NULL CONSTRAINT enum_import_preview_state CHECK (state IN ('warning', 'error', 'done')), + created timestamptz NOT NULL, + result jsonb +); + + + + +CREATE TABLE list_of_speakers_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + closed boolean DEFAULT False, + sequential_number integer NOT NULL, + CONSTRAINT unique_list_of_speakers_sequential_number UNIQUE (sequential_number, meeting_id), + moderator_notes text, + content_object_id varchar(100) NOT NULL, + content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_meeting_mediafile_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','meeting_mediafile')), + meeting_id integer NOT NULL +); + + + +comment on column list_of_speakers_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + + +CREATE TABLE mediafile_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + title varchar(256), + is_directory boolean, + filesize integer, + filename varchar(256), + mimetype varchar(256), + pdf_information jsonb, + create_timestamp timestamptz, + token varchar(256), + published_to_meetings_in_organization_id integer, + parent_id integer, + owner_id varchar(100) NOT NULL, + owner_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'meeting' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, + owner_id_organization_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'organization' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_owner_id_part1 CHECK (split_part(owner_id, '/', 1) IN ('meeting','organization')) +); + + + +comment on column mediafile_t.title is 'Title and parent_id must be unique.'; +comment on column mediafile_t.filesize is 'In bytes, not the human readable format anymore.'; +comment on column mediafile_t.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; + + +CREATE TABLE meeting_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + external_id varchar(256), + welcome_title varchar(256) DEFAULT 'Welcome to OpenSlides', + welcome_text text DEFAULT 'Space for your welcome text.', + name varchar(200) NOT NULL DEFAULT 'OpenSlides', + is_active_in_organization_id integer, + is_archived_in_organization_id integer, + description varchar(100), + location varchar(256), + start_time timestamptz, + end_time timestamptz, + locked_from_inside boolean, + imported_at timestamptz, + language varchar(256) CONSTRAINT enum_meeting_language CHECK (language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', + jitsi_domain varchar(256), + jitsi_room_name varchar(256), + jitsi_room_password varchar(256), + template_for_organization_id integer, + enable_anonymous boolean DEFAULT False, + custom_translations jsonb, + conference_show boolean DEFAULT False, + conference_auto_connect boolean DEFAULT False, + conference_los_restriction boolean DEFAULT True, + conference_stream_url varchar(256), + conference_stream_poster_url varchar(256), + conference_open_microphone boolean DEFAULT False, + conference_open_video boolean DEFAULT False, + conference_auto_connect_next_speakers integer DEFAULT 0, + conference_enable_helpdesk boolean DEFAULT False, + applause_enable boolean DEFAULT False, + applause_type varchar(256) CONSTRAINT enum_meeting_applause_type CHECK (applause_type IN ('applause-type-bar', 'applause-type-particles')) DEFAULT 'applause-type-bar', + applause_show_level boolean DEFAULT False, + applause_min_amount integer CONSTRAINT minimum_applause_min_amount CHECK (applause_min_amount >= 0) DEFAULT 1, + applause_max_amount integer CONSTRAINT minimum_applause_max_amount CHECK (applause_max_amount >= 0) DEFAULT 0, + applause_timeout integer CONSTRAINT minimum_applause_timeout CHECK (applause_timeout >= 0) DEFAULT 5, + applause_particle_image_url varchar(256), + projector_countdown_default_time integer NOT NULL DEFAULT 60, + projector_countdown_warning_time integer NOT NULL CONSTRAINT minimum_projector_countdown_warning_time CHECK (projector_countdown_warning_time >= 0) DEFAULT 0, + export_csv_encoding varchar(256) CONSTRAINT enum_meeting_export_csv_encoding CHECK (export_csv_encoding IN ('utf-8', 'iso-8859-15')) DEFAULT 'utf-8', + export_csv_separator varchar(256) DEFAULT ';', + export_pdf_pagenumber_alignment varchar(256) CONSTRAINT enum_meeting_export_pdf_pagenumber_alignment CHECK (export_pdf_pagenumber_alignment IN ('left', 'right', 'center')) DEFAULT 'center', export_pdf_fontsize integer CONSTRAINT enum_meeting_export_pdf_fontsize CHECK (export_pdf_fontsize IN (10, 11, 12)) DEFAULT 10, export_pdf_line_height double precision CONSTRAINT minimum_export_pdf_line_height CHECK (export_pdf_line_height >= 1.0) DEFAULT 1.25, export_pdf_page_margin_left integer CONSTRAINT minimum_export_pdf_page_margin_left CHECK (export_pdf_page_margin_left >= 0) DEFAULT 20, @@ -630,213 +681,186 @@ comment on column meeting_t.list_of_speakers_intervention_time is '0 disables in comment on column meeting_t.poll_default_live_voting_enabled is 'Defines default ''poll.live_voting_enabled'' option suggested to user. Is not used in the validations.'; -CREATE TABLE structure_level_t ( +CREATE TABLE meeting_mediafile_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, - color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$'), - default_time integer CONSTRAINT minimum_default_time CHECK (default_time >= 0), - meeting_id integer NOT NULL + mediafile_id integer NOT NULL, + meeting_id integer NOT NULL, + is_public boolean NOT NULL ); +comment on column meeting_mediafile_t.is_public is 'Calculated in actions. Used to discern whether the (meeting-)mediafile can be seen by everyone, because, in the case of inherited_access_group_ids == [], it would otherwise not be clear. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; + -CREATE TABLE group_t ( +CREATE TABLE meeting_user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - external_id varchar(256), - name varchar(256) NOT NULL, - permissions varchar(256)[] CONSTRAINT enum_group_permissions CHECK (permissions <@ ARRAY['agenda_item.can_manage', 'agenda_item.can_see', 'agenda_item.can_see_internal', 'assignment.can_manage', 'assignment.can_manage_polls', 'assignment.can_nominate_other', 'assignment.can_nominate_self', 'assignment.can_see', 'chat.can_manage', 'list_of_speakers.can_be_speaker', 'list_of_speakers.can_manage', 'list_of_speakers.can_see', 'list_of_speakers.can_manage_moderator_notes', 'list_of_speakers.can_see_moderator_notes', 'mediafile.can_manage', 'mediafile.can_see', 'meeting.can_manage_logos_and_fonts', 'meeting.can_manage_settings', 'meeting.can_see_autopilot', 'meeting.can_see_frontpage', 'meeting.can_see_history', 'meeting.can_see_livestream', 'motion.can_create', 'motion.can_create_amendments', 'motion.can_forward', 'motion.can_manage', 'motion.can_manage_metadata', 'motion.can_manage_polls', 'motion.can_see', 'motion.can_see_internal', 'motion.can_see_origin', 'motion.can_support', 'poll.can_manage', 'poll.can_see_progress', 'projector.can_manage', 'projector.can_see', 'tag.can_manage', 'user.can_manage', 'user.can_manage_presence', 'user.can_see_sensitive_data', 'user.can_see', 'user.can_update', 'user.can_edit_own_delegation']::varchar[]), - weight integer, - used_as_motion_poll_default_id integer, - used_as_assignment_poll_default_id integer, - used_as_topic_poll_default_id integer, - used_as_poll_default_id integer, - meeting_id integer NOT NULL + comment text, + number varchar(256), + about_me text, + vote_weight decimal(16,6) CONSTRAINT minimum_vote_weight CHECK (vote_weight >= 0.000001), + locked_out boolean, + user_id integer NOT NULL, + meeting_id integer NOT NULL, + vote_delegated_to_id integer ); -comment on column group_t.external_id is 'unique in meeting'; - -CREATE TABLE personal_note_t ( +CREATE TABLE motion_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - note text, - star boolean, - meeting_user_id integer NOT NULL, - content_object_id varchar(100), - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion')), + number varchar(256), + number_value integer, + sequential_number integer NOT NULL, + CONSTRAINT unique_motion_sequential_number UNIQUE (sequential_number, meeting_id), + title varchar(256) NOT NULL, + text text, + text_hash varchar(256), + amendment_paragraphs jsonb, + modified_final_version text, + reason text, + category_weight integer DEFAULT 10000, + state_extension varchar(256), + recommendation_extension varchar(256), + sort_weight integer DEFAULT 10000, + created timestamptz, + last_modified timestamptz, + workflow_timestamp timestamptz, + start_line_number integer CONSTRAINT minimum_start_line_number CHECK (start_line_number >= 1) DEFAULT 1, + forwarded timestamptz, + additional_submitter varchar(256), + marked_forwarded boolean, + lead_motion_id integer, + sort_parent_id integer, + origin_id integer, + origin_meeting_id integer, + state_id integer NOT NULL, + recommendation_id integer, + category_id integer, + block_id integer, meeting_id integer NOT NULL ); +comment on column motion_t.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; +comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column motion_t.marked_forwarded is 'Forwarded amendments can be marked as such. This is just optional, however. Forwarded amendments can also have this field set to false.'; + -CREATE TABLE tag_t ( +CREATE TABLE motion_block_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, + title varchar(256) NOT NULL, + internal boolean, + sequential_number integer NOT NULL, + CONSTRAINT unique_motion_block_sequential_number UNIQUE (sequential_number, meeting_id), meeting_id integer NOT NULL ); +comment on column motion_block_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE agenda_item_t ( + +CREATE TABLE motion_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - item_number varchar(256), - comment varchar(256), - closed boolean DEFAULT False, - type varchar(256) CONSTRAINT enum_agenda_item_type CHECK (type IN ('common', 'internal', 'hidden')) DEFAULT 'common', - duration integer CONSTRAINT minimum_duration CHECK (duration >= 0), - is_internal boolean, - is_hidden boolean, + name varchar(256) NOT NULL, + prefix varchar(256), + weight integer DEFAULT 10000, level integer, - weight integer, - content_object_id varchar(100) NOT NULL, - content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic')), + sequential_number integer NOT NULL, + CONSTRAINT unique_motion_category_sequential_number UNIQUE (sequential_number, meeting_id), parent_id integer, meeting_id integer NOT NULL ); -comment on column agenda_item_t.duration is 'Given in seconds'; -comment on column agenda_item_t.is_internal is 'Calculated by the server'; -comment on column agenda_item_t.is_hidden is 'Calculated by the server'; -comment on column agenda_item_t.level is 'Calculated by the server'; +comment on column motion_category_t.level is 'Calculated field.'; +comment on column motion_category_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE list_of_speakers_t ( +CREATE TABLE motion_change_recommendation_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - closed boolean DEFAULT False, - sequential_number integer NOT NULL, - CONSTRAINT unique_list_of_speakers_sequential_number UNIQUE (sequential_number, meeting_id), - moderator_notes text, - content_object_id varchar(100) NOT NULL, - content_object_id_motion_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_block_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_assignment_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_topic_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_meeting_mediafile_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','motion_block','assignment','topic','meeting_mediafile')), + rejected boolean DEFAULT False, + internal boolean DEFAULT False, + type varchar(256) CONSTRAINT enum_motion_change_recommendation_type CHECK (type IN ('replacement', 'insertion', 'deletion', 'other')) DEFAULT 'replacement', + other_description varchar(256), + line_from integer CONSTRAINT minimum_line_from CHECK (line_from >= 0), + line_to integer CONSTRAINT minimum_line_to CHECK (line_to >= 0), + text text, + creation_time timestamptz, + motion_id integer NOT NULL, meeting_id integer NOT NULL ); -comment on column list_of_speakers_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - -CREATE TABLE structure_level_list_of_speakers_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - structure_level_id integer NOT NULL, - list_of_speakers_id integer NOT NULL, - initial_time integer NOT NULL CONSTRAINT minimum_initial_time CHECK (initial_time >= 1), - additional_time double precision, - remaining_time double precision NOT NULL, - current_start_time timestamptz, - meeting_id integer NOT NULL -); - - - -comment on column structure_level_list_of_speakers_t.initial_time is 'The initial time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakers_t.additional_time is 'The summed added time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakers_t.remaining_time is 'The currently remaining time of this structure_level for this LoS'; -comment on column structure_level_list_of_speakers_t.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; - - -CREATE TABLE point_of_order_category_t ( +CREATE TABLE motion_comment_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - text varchar(256) NOT NULL, - rank integer NOT NULL, + comment text, + motion_id integer NOT NULL, + section_id integer NOT NULL, meeting_id integer NOT NULL ); -CREATE TABLE speaker_t ( +CREATE TABLE motion_comment_section_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - begin_time timestamptz, - end_time timestamptz, - pause_time timestamptz, - unpause_time timestamptz, - total_pause integer, + name varchar(256) NOT NULL, weight integer DEFAULT 10000, - speech_state varchar(256) CONSTRAINT enum_speaker_speech_state CHECK (speech_state IN ('contribution', 'pro', 'contra', 'intervention', 'interposed_question')), - answer boolean, - note varchar(250), - point_of_order boolean, - list_of_speakers_id integer NOT NULL, - structure_level_list_of_speakers_id integer, - meeting_user_id integer, - point_of_order_category_id integer, + sequential_number integer NOT NULL, + CONSTRAINT unique_motion_comment_section_sequential_number UNIQUE (sequential_number, meeting_id), + submitter_can_write boolean, meeting_id integer NOT NULL ); +comment on column motion_comment_section_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + -CREATE TABLE topic_t ( +CREATE TABLE motion_editor_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - title varchar(256) NOT NULL, - text text, - sequential_number integer NOT NULL, - CONSTRAINT unique_topic_sequential_number UNIQUE (sequential_number, meeting_id), + weight integer, + meeting_user_id integer, + motion_id integer NOT NULL, meeting_id integer NOT NULL ); -comment on column topic_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - -CREATE TABLE motion_t ( +CREATE TABLE motion_state_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - number varchar(256), - number_value integer, - sequential_number integer NOT NULL, - CONSTRAINT unique_motion_sequential_number UNIQUE (sequential_number, meeting_id), - title varchar(256) NOT NULL, - text text, - text_hash varchar(256), - amendment_paragraphs jsonb, - modified_final_version text, - reason text, - category_weight integer DEFAULT 10000, - state_extension varchar(256), - recommendation_extension varchar(256), - sort_weight integer DEFAULT 10000, - created timestamptz, - last_modified timestamptz, - workflow_timestamp timestamptz, - start_line_number integer CONSTRAINT minimum_start_line_number CHECK (start_line_number >= 1) DEFAULT 1, - forwarded timestamptz, - additional_submitter varchar(256), - marked_forwarded boolean, - lead_motion_id integer, - sort_parent_id integer, - origin_id integer, - origin_meeting_id integer, - state_id integer NOT NULL, - recommendation_id integer, - category_id integer, - block_id integer, + name varchar(256) NOT NULL, + weight integer NOT NULL, + recommendation_label varchar(256), + is_internal boolean, + css_class varchar(256) NOT NULL CONSTRAINT enum_motion_state_css_class CHECK (css_class IN ('grey', 'red', 'green', 'lightblue', 'yellow')) DEFAULT 'lightblue', + restrictions varchar(256)[] CONSTRAINT enum_motion_state_restrictions CHECK (restrictions <@ ARRAY['motion.can_see_internal', 'motion.can_manage_metadata', 'motion.can_manage', 'is_submitter']::varchar[]) DEFAULT '{}', + allow_support boolean DEFAULT False, + allow_create_poll boolean DEFAULT False, + allow_submitter_edit boolean DEFAULT False, + set_number boolean DEFAULT True, + show_state_extension_field boolean DEFAULT False, + show_recommendation_extension_field boolean DEFAULT False, + merge_amendment_into_final varchar(256) CONSTRAINT enum_motion_state_merge_amendment_into_final CHECK (merge_amendment_into_final IN ('do_not_merge', 'undefined', 'do_merge')) DEFAULT 'undefined', + allow_motion_forwarding boolean DEFAULT False, + allow_amendment_forwarding boolean, + set_workflow_timestamp boolean DEFAULT False, + state_button_label varchar(256), + submitter_withdraw_state_id integer, + workflow_id integer NOT NULL, meeting_id integer NOT NULL ); -comment on column motion_t.number_value is 'The number value of this motion. This number is auto-generated and read-only.'; -comment on column motion_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -comment on column motion_t.marked_forwarded is 'Forwarded amendments can be marked as such. This is just optional, however. Forwarded amendments can also have this field set to false.'; - CREATE TABLE motion_submitter_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -859,16 +883,19 @@ CREATE TABLE motion_supporter_t ( -CREATE TABLE motion_editor_t ( +CREATE TABLE motion_workflow_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight integer, - meeting_user_id integer, - motion_id integer NOT NULL, + name varchar(256) NOT NULL, + sequential_number integer NOT NULL, + CONSTRAINT unique_motion_workflow_sequential_number UNIQUE (sequential_number, meeting_id), + first_state_id integer NOT NULL UNIQUE, meeting_id integer NOT NULL ); +comment on column motion_workflow_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + CREATE TABLE motion_working_group_speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -881,121 +908,104 @@ CREATE TABLE motion_working_group_speaker_t ( -CREATE TABLE motion_comment_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - comment text, - motion_id integer NOT NULL, - section_id integer NOT NULL, - meeting_id integer NOT NULL -); - - - - -CREATE TABLE motion_comment_section_t ( +CREATE TABLE option_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, weight integer DEFAULT 10000, - sequential_number integer NOT NULL, - CONSTRAINT unique_motion_comment_section_sequential_number UNIQUE (sequential_number, meeting_id), - submitter_can_write boolean, + text text, + yes decimal(16,6), + no decimal(16,6), + abstain decimal(16,6), + poll_id integer, + used_as_global_option_in_poll_id integer UNIQUE, + content_object_id varchar(100), + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_candidate_list_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), meeting_id integer NOT NULL ); -comment on column motion_comment_section_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - -CREATE TABLE motion_category_t ( +CREATE TABLE organization_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, - prefix varchar(256), - weight integer DEFAULT 10000, - level integer, - sequential_number integer NOT NULL, - CONSTRAINT unique_motion_category_sequential_number UNIQUE (sequential_number, meeting_id), - parent_id integer, - meeting_id integer NOT NULL -); - + name varchar(256), + description text, + legal_notice text, + privacy_policy text, + login_text text, + reset_password_verbose_errors boolean, + disable_forward_with_attachments boolean, + enable_electronic_voting boolean, + enable_chat boolean, + limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, + limit_of_users integer CONSTRAINT minimum_limit_of_users CHECK (limit_of_users >= 0) DEFAULT 0, + default_language varchar(256) CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', + require_duplicate_from boolean, + enable_anonymous boolean, + saml_enabled boolean, + saml_login_button_text varchar(256) DEFAULT 'SAML login', + saml_attr_mapping jsonb, + saml_metadata_idp text, + saml_metadata_sp text, + saml_private_key text, + theme_id integer NOT NULL UNIQUE, + users_email_sender varchar(256) DEFAULT 'OpenSlides', + users_email_replyto varchar(256), + users_email_subject varchar(256) DEFAULT 'OpenSlides access data', + users_email_body text DEFAULT 'Dear {name}, +this is your personal OpenSlides login: -comment on column motion_category_t.level is 'Calculated field.'; -comment on column motion_category_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +{url} +Username: {username} +Password: {password} -CREATE TABLE motion_block_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - title varchar(256) NOT NULL, - internal boolean, - sequential_number integer NOT NULL, - CONSTRAINT unique_motion_block_sequential_number UNIQUE (sequential_number, meeting_id), - meeting_id integer NOT NULL +This email was generated automatically.', + url varchar(256) DEFAULT 'https://example.com' ); -comment on column motion_block_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; +comment on column organization_t.limit_of_meetings is 'Maximum of active meetings for the whole organization. 0 means no limitation at all'; +comment on column organization_t.limit_of_users is 'Maximum of active users for the whole organization. 0 means no limitation at all'; -CREATE TABLE motion_change_recommendation_t ( +CREATE TABLE organization_tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - rejected boolean DEFAULT False, - internal boolean DEFAULT False, - type varchar(256) CONSTRAINT enum_motion_change_recommendation_type CHECK (type IN ('replacement', 'insertion', 'deletion', 'other')) DEFAULT 'replacement', - other_description varchar(256), - line_from integer CONSTRAINT minimum_line_from CHECK (line_from >= 0), - line_to integer CONSTRAINT minimum_line_to CHECK (line_to >= 0), - text text, - creation_time timestamptz, - motion_id integer NOT NULL, - meeting_id integer NOT NULL + name varchar(256) NOT NULL, + color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$') NOT NULL, + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); -CREATE TABLE motion_state_t ( +CREATE TABLE personal_note_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, - weight integer NOT NULL, - recommendation_label varchar(256), - is_internal boolean, - css_class varchar(256) NOT NULL CONSTRAINT enum_motion_state_css_class CHECK (css_class IN ('grey', 'red', 'green', 'lightblue', 'yellow')) DEFAULT 'lightblue', - restrictions varchar(256)[] CONSTRAINT enum_motion_state_restrictions CHECK (restrictions <@ ARRAY['motion.can_see_internal', 'motion.can_manage_metadata', 'motion.can_manage', 'is_submitter']::varchar[]) DEFAULT '{}', - allow_support boolean DEFAULT False, - allow_create_poll boolean DEFAULT False, - allow_submitter_edit boolean DEFAULT False, - set_number boolean DEFAULT True, - show_state_extension_field boolean DEFAULT False, - show_recommendation_extension_field boolean DEFAULT False, - merge_amendment_into_final varchar(256) CONSTRAINT enum_motion_state_merge_amendment_into_final CHECK (merge_amendment_into_final IN ('do_not_merge', 'undefined', 'do_merge')) DEFAULT 'undefined', - allow_motion_forwarding boolean DEFAULT False, - allow_amendment_forwarding boolean, - set_workflow_timestamp boolean DEFAULT False, - state_button_label varchar(256), - submitter_withdraw_state_id integer, - workflow_id integer NOT NULL, + note text, + star boolean, + meeting_user_id integer NOT NULL, + content_object_id varchar(100), + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion')), meeting_id integer NOT NULL ); -CREATE TABLE motion_workflow_t ( +CREATE TABLE point_of_order_category_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL, - sequential_number integer NOT NULL, - CONSTRAINT unique_motion_workflow_sequential_number UNIQUE (sequential_number, meeting_id), - first_state_id integer NOT NULL UNIQUE, + text varchar(256) NOT NULL, + rank integer NOT NULL, meeting_id integer NOT NULL ); -comment on column motion_workflow_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - CREATE TABLE poll_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1041,63 +1051,11 @@ comment on column poll_t.sequential_number is 'The (positive) serial number of t */ -CREATE TABLE option_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight integer DEFAULT 10000, - text text, - yes decimal(16,6), - no decimal(16,6), - abstain decimal(16,6), - poll_id integer, - used_as_global_option_in_poll_id integer UNIQUE, - content_object_id varchar(100), - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'user' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_poll_candidate_list_id integer UNIQUE GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll_candidate_list' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('motion','user','poll_candidate_list')), - meeting_id integer NOT NULL -); - - - - -CREATE TABLE vote_t ( +CREATE TABLE poll_candidate_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight decimal(16,6), - value varchar(256), - user_token varchar(256) NOT NULL, - option_id integer NOT NULL, + poll_candidate_list_id integer NOT NULL, user_id integer, - delegated_user_id integer, - meeting_id integer NOT NULL -); - - - - -CREATE TABLE assignment_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - title varchar(256) NOT NULL, - description text, - open_posts integer CONSTRAINT minimum_open_posts CHECK (open_posts >= 0) DEFAULT 0, - phase varchar(256) CONSTRAINT enum_assignment_phase CHECK (phase IN ('search', 'voting', 'finished')) DEFAULT 'search', - default_poll_description text, - number_poll_candidates boolean, - sequential_number integer NOT NULL, - CONSTRAINT unique_assignment_sequential_number UNIQUE (sequential_number, meeting_id), - meeting_id integer NOT NULL -); - - - -comment on column assignment_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; - - -CREATE TABLE assignment_candidate_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - weight integer DEFAULT 10000, - assignment_id integer NOT NULL, - meeting_user_id integer, + weight integer NOT NULL, meeting_id integer NOT NULL ); @@ -1112,53 +1070,39 @@ CREATE TABLE poll_candidate_list_t ( -CREATE TABLE poll_candidate_t ( +CREATE TABLE projection_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - poll_candidate_list_id integer NOT NULL, - user_id integer, - weight integer NOT NULL, + options jsonb, + stable boolean DEFAULT False, + weight integer, + type varchar(256), + current_projector_id integer, + preview_projector_id integer, + history_projector_id integer, + content_object_id varchar(100) NOT NULL, + content_object_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_meeting_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_list_of_speakers_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'list_of_speakers' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'agenda_item' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_poll_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_projector_message_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_message' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + content_object_id_projector_countdown_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_countdown' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, + CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('meeting','motion','meeting_mediafile','list_of_speakers','motion_block','assignment','agenda_item','topic','poll','projector_message','projector_countdown')), meeting_id integer NOT NULL ); +/* + Fields without SQL definition for table projection -CREATE TABLE mediafile_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - title varchar(256), - is_directory boolean, - filesize integer, - filename varchar(256), - mimetype varchar(256), - pdf_information jsonb, - create_timestamp timestamptz, - token varchar(256), - published_to_meetings_in_organization_id integer, - parent_id integer, - owner_id varchar(100) NOT NULL, - owner_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'meeting' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, - owner_id_organization_id integer GENERATED ALWAYS AS (CASE WHEN split_part(owner_id, '/', 1) = 'organization' THEN cast(split_part(owner_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_owner_id_part1 CHECK (split_part(owner_id, '/', 1) IN ('meeting','organization')) -); - - - -comment on column mediafile_t.title is 'Title and parent_id must be unique.'; -comment on column mediafile_t.filesize is 'In bytes, not the human readable format anymore.'; -comment on column mediafile_t.filename is 'The uploaded filename. Will be used for downloading. Only writeable on create.'; - - -CREATE TABLE meeting_mediafile_t ( - id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - mediafile_id integer NOT NULL, - meeting_id integer NOT NULL, - is_public boolean NOT NULL -); - - - -comment on column meeting_mediafile_t.is_public is 'Calculated in actions. Used to discern whether the (meeting-)mediafile can be seen by everyone, because, in the case of inherited_access_group_ids == [], it would otherwise not be clear. inherited_access_group_ids == [] can have two causes: cancelling access groups (=> is_public := false) or no access groups at all (=> is_public := true)'; + projection/content: type:JSON is marked as a calculated field and not generated in schema +*/ CREATE TABLE projector_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1206,39 +1150,18 @@ CREATE TABLE projector_t ( comment on column projector_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; -CREATE TABLE projection_t ( +CREATE TABLE projector_countdown_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - options jsonb, - stable boolean DEFAULT False, - weight integer, - type varchar(256), - current_projector_id integer, - preview_projector_id integer, - history_projector_id integer, - content_object_id varchar(100) NOT NULL, - content_object_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_meeting_mediafile_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'meeting_mediafile' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_list_of_speakers_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'list_of_speakers' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_motion_block_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'motion_block' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'assignment' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'agenda_item' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'topic' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_poll_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'poll' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_projector_message_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_message' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - content_object_id_projector_countdown_id integer GENERATED ALWAYS AS (CASE WHEN split_part(content_object_id, '/', 1) = 'projector_countdown' THEN cast(split_part(content_object_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_content_object_id_part1 CHECK (split_part(content_object_id, '/', 1) IN ('meeting','motion','meeting_mediafile','list_of_speakers','motion_block','assignment','agenda_item','topic','poll','projector_message','projector_countdown')), + title varchar(256) NOT NULL, + description varchar(256) DEFAULT '', + default_time integer, + countdown_time double precision DEFAULT 60, + running boolean DEFAULT False, meeting_id integer NOT NULL ); -/* - Fields without SQL definition for table projection - - projection/content: type:JSON is marked as a calculated field and not generated in schema - -*/ CREATE TABLE projector_message_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -1249,88 +1172,177 @@ CREATE TABLE projector_message_t ( -CREATE TABLE projector_countdown_t ( +CREATE TABLE speaker_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - title varchar(256) NOT NULL, - description varchar(256) DEFAULT '', - default_time integer, - countdown_time double precision DEFAULT 60, - running boolean DEFAULT False, + begin_time timestamptz, + end_time timestamptz, + pause_time timestamptz, + unpause_time timestamptz, + total_pause integer, + weight integer DEFAULT 10000, + speech_state varchar(256) CONSTRAINT enum_speaker_speech_state CHECK (speech_state IN ('contribution', 'pro', 'contra', 'intervention', 'interposed_question')), + answer boolean, + note varchar(250), + point_of_order boolean, + list_of_speakers_id integer NOT NULL, + structure_level_list_of_speakers_id integer, + meeting_user_id integer, + point_of_order_category_id integer, meeting_id integer NOT NULL ); -CREATE TABLE chat_group_t ( +CREATE TABLE structure_level_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - weight integer DEFAULT 10000, + color varchar(7) CHECK (color is null or color ~* '^#[a-f0-9]{6}$'), + default_time integer CONSTRAINT minimum_default_time CHECK (default_time >= 0), meeting_id integer NOT NULL ); -CREATE TABLE chat_message_t ( +CREATE TABLE structure_level_list_of_speakers_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - content text NOT NULL, - created timestamptz NOT NULL, - meeting_user_id integer, - chat_group_id integer NOT NULL, + structure_level_id integer NOT NULL, + list_of_speakers_id integer NOT NULL, + initial_time integer NOT NULL CONSTRAINT minimum_initial_time CHECK (initial_time >= 1), + additional_time double precision, + remaining_time double precision NOT NULL, + current_start_time timestamptz, meeting_id integer NOT NULL ); +comment on column structure_level_list_of_speakers_t.initial_time is 'The initial time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.additional_time is 'The summed added time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.remaining_time is 'The currently remaining time of this structure_level for this LoS'; +comment on column structure_level_list_of_speakers_t.current_start_time is 'The current start time of a speaker for this structure_level. Is only set if a currently speaking speaker exists'; -CREATE TABLE action_worker_t ( + +CREATE TABLE tag_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, name varchar(256) NOT NULL, - state varchar(256) NOT NULL CONSTRAINT enum_action_worker_state CHECK (state IN ('running', 'end', 'aborted')), - created timestamptz NOT NULL, - timestamp timestamptz NOT NULL, - result jsonb, - user_id integer NOT NULL + meeting_id integer NOT NULL ); -comment on column action_worker_t.user_id is 'Id of the calling user. If the action is called via internal route, the value will be -1.'; + +CREATE TABLE theme_t ( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name varchar(256) NOT NULL, + accent_100 varchar(7) CHECK (accent_100 is null or accent_100 ~* '^#[a-f0-9]{6}$'), + accent_200 varchar(7) CHECK (accent_200 is null or accent_200 ~* '^#[a-f0-9]{6}$'), + accent_300 varchar(7) CHECK (accent_300 is null or accent_300 ~* '^#[a-f0-9]{6}$'), + accent_400 varchar(7) CHECK (accent_400 is null or accent_400 ~* '^#[a-f0-9]{6}$'), + accent_50 varchar(7) CHECK (accent_50 is null or accent_50 ~* '^#[a-f0-9]{6}$'), + accent_500 varchar(7) CHECK (accent_500 is null or accent_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#2196f3', + accent_600 varchar(7) CHECK (accent_600 is null or accent_600 ~* '^#[a-f0-9]{6}$'), + accent_700 varchar(7) CHECK (accent_700 is null or accent_700 ~* '^#[a-f0-9]{6}$'), + accent_800 varchar(7) CHECK (accent_800 is null or accent_800 ~* '^#[a-f0-9]{6}$'), + accent_900 varchar(7) CHECK (accent_900 is null or accent_900 ~* '^#[a-f0-9]{6}$'), + accent_a100 varchar(7) CHECK (accent_a100 is null or accent_a100 ~* '^#[a-f0-9]{6}$'), + accent_a200 varchar(7) CHECK (accent_a200 is null or accent_a200 ~* '^#[a-f0-9]{6}$'), + accent_a400 varchar(7) CHECK (accent_a400 is null or accent_a400 ~* '^#[a-f0-9]{6}$'), + accent_a700 varchar(7) CHECK (accent_a700 is null or accent_a700 ~* '^#[a-f0-9]{6}$'), + primary_100 varchar(7) CHECK (primary_100 is null or primary_100 ~* '^#[a-f0-9]{6}$'), + primary_200 varchar(7) CHECK (primary_200 is null or primary_200 ~* '^#[a-f0-9]{6}$'), + primary_300 varchar(7) CHECK (primary_300 is null or primary_300 ~* '^#[a-f0-9]{6}$'), + primary_400 varchar(7) CHECK (primary_400 is null or primary_400 ~* '^#[a-f0-9]{6}$'), + primary_50 varchar(7) CHECK (primary_50 is null or primary_50 ~* '^#[a-f0-9]{6}$'), + primary_500 varchar(7) CHECK (primary_500 is null or primary_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#317796', + primary_600 varchar(7) CHECK (primary_600 is null or primary_600 ~* '^#[a-f0-9]{6}$'), + primary_700 varchar(7) CHECK (primary_700 is null or primary_700 ~* '^#[a-f0-9]{6}$'), + primary_800 varchar(7) CHECK (primary_800 is null or primary_800 ~* '^#[a-f0-9]{6}$'), + primary_900 varchar(7) CHECK (primary_900 is null or primary_900 ~* '^#[a-f0-9]{6}$'), + primary_a100 varchar(7) CHECK (primary_a100 is null or primary_a100 ~* '^#[a-f0-9]{6}$'), + primary_a200 varchar(7) CHECK (primary_a200 is null or primary_a200 ~* '^#[a-f0-9]{6}$'), + primary_a400 varchar(7) CHECK (primary_a400 is null or primary_a400 ~* '^#[a-f0-9]{6}$'), + primary_a700 varchar(7) CHECK (primary_a700 is null or primary_a700 ~* '^#[a-f0-9]{6}$'), + warn_100 varchar(7) CHECK (warn_100 is null or warn_100 ~* '^#[a-f0-9]{6}$'), + warn_200 varchar(7) CHECK (warn_200 is null or warn_200 ~* '^#[a-f0-9]{6}$'), + warn_300 varchar(7) CHECK (warn_300 is null or warn_300 ~* '^#[a-f0-9]{6}$'), + warn_400 varchar(7) CHECK (warn_400 is null or warn_400 ~* '^#[a-f0-9]{6}$'), + warn_50 varchar(7) CHECK (warn_50 is null or warn_50 ~* '^#[a-f0-9]{6}$'), + warn_500 varchar(7) CHECK (warn_500 is null or warn_500 ~* '^#[a-f0-9]{6}$') DEFAULT '#f06400', + warn_600 varchar(7) CHECK (warn_600 is null or warn_600 ~* '^#[a-f0-9]{6}$'), + warn_700 varchar(7) CHECK (warn_700 is null or warn_700 ~* '^#[a-f0-9]{6}$'), + warn_800 varchar(7) CHECK (warn_800 is null or warn_800 ~* '^#[a-f0-9]{6}$'), + warn_900 varchar(7) CHECK (warn_900 is null or warn_900 ~* '^#[a-f0-9]{6}$'), + warn_a100 varchar(7) CHECK (warn_a100 is null or warn_a100 ~* '^#[a-f0-9]{6}$'), + warn_a200 varchar(7) CHECK (warn_a200 is null or warn_a200 ~* '^#[a-f0-9]{6}$'), + warn_a400 varchar(7) CHECK (warn_a400 is null or warn_a400 ~* '^#[a-f0-9]{6}$'), + warn_a700 varchar(7) CHECK (warn_a700 is null or warn_a700 ~* '^#[a-f0-9]{6}$'), + headbar varchar(7) CHECK (headbar is null or headbar ~* '^#[a-f0-9]{6}$'), + yes varchar(7) CHECK (yes is null or yes ~* '^#[a-f0-9]{6}$'), + no varchar(7) CHECK (no is null or no ~* '^#[a-f0-9]{6}$'), + abstain varchar(7) CHECK (abstain is null or abstain ~* '^#[a-f0-9]{6}$'), + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL +); -CREATE TABLE import_preview_t ( + + +CREATE TABLE topic_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name varchar(256) NOT NULL CONSTRAINT enum_import_preview_name CHECK (name IN ('account', 'participant', 'topic', 'committee', 'motion')), - state varchar(256) NOT NULL CONSTRAINT enum_import_preview_state CHECK (state IN ('warning', 'error', 'done')), - created timestamptz NOT NULL, - result jsonb + title varchar(256) NOT NULL, + text text, + sequential_number integer NOT NULL, + CONSTRAINT unique_topic_sequential_number UNIQUE (sequential_number, meeting_id), + meeting_id integer NOT NULL ); +comment on column topic_t.sequential_number is 'The (positive) serial number of this model in its meeting. This number is auto-generated and read-only.'; + -CREATE TABLE history_position_t ( +CREATE TABLE user_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - timestamp timestamptz, - original_user_id integer, - user_id integer + username varchar(256) NOT NULL, + member_number varchar(256), + saml_id varchar(256) CONSTRAINT minlength_saml_id CHECK (char_length(saml_id) >= 1), + pronoun varchar(32), + title varchar(256), + first_name varchar(256), + last_name varchar(256), + is_active boolean DEFAULT True, + is_physical_person boolean DEFAULT True, + password varchar(256), + default_password varchar(256), + can_change_own_password boolean DEFAULT True, + email varchar(256), + default_vote_weight decimal(16,6) CONSTRAINT minimum_default_vote_weight CHECK (default_vote_weight >= 0.000001) DEFAULT '1.000000', + last_email_sent timestamptz, + is_demo_user boolean, + last_login timestamptz, + external boolean, + gender_id integer, + organization_management_level varchar(256) CONSTRAINT enum_user_organization_management_level CHECK (organization_management_level IN ('superadmin', 'can_manage_organization', 'can_manage_users')), + home_committee_id integer, + organization_id integer GENERATED ALWAYS AS (1) STORED NOT NULL ); +comment on column user_t.saml_id is 'unique-key from IdP for SAML login'; +comment on column user_t.organization_management_level is 'Hierarchical permission level for the whole organization.'; -CREATE TABLE history_entry_t ( + +CREATE TABLE vote_t ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, - entries text[], - original_model_id varchar(256), - model_id varchar(100), - model_id_user_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'user' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, - model_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'motion' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, - model_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(model_id, '/', 1) = 'assignment' THEN cast(split_part(model_id, '/', 2) AS INTEGER) ELSE null END) STORED, - CONSTRAINT valid_model_id_part1 CHECK (split_part(model_id, '/', 1) IN ('user','motion','assignment')), - position_id integer NOT NULL, - meeting_id integer + weight decimal(16,6), + value varchar(256), + user_token varchar(256) NOT NULL, + option_id integer NOT NULL, + user_id integer, + delegated_user_id integer, + meeting_id integer NOT NULL ); @@ -1339,20 +1351,16 @@ CREATE TABLE history_entry_t ( -- Intermediate table definitions -CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( - meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (meeting_user_id, structure_level_id) +CREATE TABLE nm_chat_group_read_group_ids_group_t ( + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (chat_group_id, group_id) ); -CREATE TABLE gm_organization_tag_tagged_ids_t ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - tagged_id varchar(100) NOT NULL, - tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), - CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) +CREATE TABLE nm_chat_group_write_group_ids_group_t ( + chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (chat_group_id, group_id) ); CREATE TABLE nm_committee_manager_ids_user_t ( @@ -1373,12 +1381,6 @@ CREATE TABLE nm_committee_forward_to_committee_ids_committee_t ( PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); -CREATE TABLE nm_meeting_present_user_ids_user_t ( - meeting_id integer NOT NULL REFERENCES meeting_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (meeting_id, user_id) -); - CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1415,15 +1417,26 @@ CREATE TABLE nm_group_poll_ids_poll_t ( PRIMARY KEY (group_id, poll_id) ); -CREATE TABLE gm_tag_tagged_ids_t ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - tag_id integer NOT NULL REFERENCES tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - tagged_id varchar(100) NOT NULL, - tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), - CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) +CREATE TABLE nm_meeting_present_user_ids_user_t ( + meeting_id integer NOT NULL REFERENCES meeting_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (meeting_id, user_id) +); + +CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( + meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + attachment_id varchar(100) NOT NULL, + attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), + CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) +); + +CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( + meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, + PRIMARY KEY (meeting_user_id, structure_level_id) ); CREATE TABLE nm_motion_all_derived_motion_ids_motion_t ( @@ -1439,7 +1452,6 @@ CREATE TABLE nm_motion_identical_motion_ids_motion_t ( ); CREATE TABLE gm_motion_state_extension_reference_ids_t ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, state_extension_reference_id varchar(100) NOT NULL, state_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(state_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(state_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1448,7 +1460,6 @@ CREATE TABLE gm_motion_state_extension_reference_ids_t ( ); CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, recommendation_extension_reference_id varchar(100) NOT NULL, recommendation_extension_reference_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(recommendation_extension_reference_id, '/', 1) = 'motion' THEN cast(split_part(recommendation_extension_reference_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1462,128 +1473,67 @@ CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( PRIMARY KEY (next_state_id, previous_state_id) ); +CREATE TABLE gm_organization_tag_tagged_ids_t ( + organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id varchar(100) NOT NULL, + tagged_id_committee_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'committee' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES committee_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_meeting_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'meeting' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES meeting_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), + CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) +); + CREATE TABLE nm_poll_voted_ids_user_t ( poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (poll_id, user_id) ); -CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - attachment_id varchar(100) NOT NULL, - attachment_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'motion' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - attachment_id_topic_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'topic' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES topic_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - attachment_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(attachment_id, '/', 1) = 'assignment' THEN cast(split_part(attachment_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, - CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), - CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) -); - -CREATE TABLE nm_chat_group_read_group_ids_group_t ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (chat_group_id, group_id) -); - -CREATE TABLE nm_chat_group_write_group_ids_group_t ( - chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, - PRIMARY KEY (chat_group_id, group_id) +CREATE TABLE gm_tag_tagged_ids_t ( + tag_id integer NOT NULL REFERENCES tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id varchar(100) NOT NULL, + tagged_id_agenda_item_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'agenda_item' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES agenda_item_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_assignment_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'assignment' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES assignment_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + tagged_id_motion_id integer GENERATED ALWAYS AS (CASE WHEN split_part(tagged_id, '/', 1) = 'motion' THEN cast(split_part(tagged_id, '/', 2) AS INTEGER) ELSE null END) STORED REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, + CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), + CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); -- View definitions -CREATE VIEW "organization" AS SELECT *, -(select array_agg(g.id ORDER BY g.id) from gender_t g where g.organization_id = o.id) as gender_ids, -(select array_agg(c.id ORDER BY c.id) from committee_t c where c.organization_id = o.id) as committee_ids, -(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, -(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, -(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, -(select array_agg(ot.id ORDER BY ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, -(select array_agg(t.id ORDER BY t.id) from theme_t t where t.organization_id = o.id) as theme_ids, -(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, -(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.published_to_meetings_in_organization_id = o.id) as published_mediafile_ids, -(select array_agg(u.id ORDER BY u.id) from user_t u where u.organization_id = o.id) as user_ids -FROM organization_t o; - - -CREATE VIEW "user" AS SELECT *, -(select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, -( - SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) - FROM ( - -- Select committee_ids from meetings the user is part of - SELECT m.committee_id - FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - INNER JOIN meeting_t AS m ON m.id = mu.meeting_id - WHERE mu.user_id = u.id - - UNION - - -- Select committee_ids from committee managers - SELECT cmu.committee_id - FROM nm_committee_manager_ids_user_t cmu - WHERE cmu.user_id = u.id - - UNION +CREATE VIEW "action_worker" AS SELECT * FROM action_worker_t a; - -- Select home_committee_id from user - SELECT u.home_committee_id - WHERE u.home_committee_id IS NOT NULL - ) _ -) AS committee_ids -, -(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, -(select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, -(select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, -(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, -(select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, -(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, -( - SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) - FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - WHERE mu.user_id = u.id -) AS meeting_ids -FROM user_t u; +CREATE VIEW "agenda_item" AS SELECT *, +(select array_agg(ai.id ORDER BY ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids +FROM agenda_item_t a; -comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; -comment on column "user".meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; -CREATE VIEW "meeting_user" AS SELECT *, -(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, -(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, -(select array_agg(ms.id ORDER BY ms.id) from motion_supporter_t ms where ms.meeting_user_id = m.id) as motion_supporter_ids, -(select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, -(select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, -(select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, -(select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, -(select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, -(select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, -(select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids -FROM meeting_user_t m; +CREATE VIEW "assignment" AS SELECT *, +(select array_agg(ac.id ORDER BY ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, +(select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, +(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_assignment_id = a.id) as history_entry_ids +FROM assignment_t a; -CREATE VIEW "gender" AS SELECT *, -(select array_agg(u.id ORDER BY u.id) from user_t u where u.gender_id = g.id) as user_ids -FROM gender_t g; +CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; -CREATE VIEW "organization_tag" AS SELECT *, -(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids -FROM organization_tag_t o; +CREATE VIEW "chat_group" AS SELECT *, +(select array_agg(cm.id ORDER BY cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids +FROM chat_group_t c; -CREATE VIEW "theme" AS SELECT *, -(select o.id from organization_t o where o.theme_id = t.id) as theme_for_organization_id -FROM theme_t t; +CREATE VIEW "chat_message" AS SELECT * FROM chat_message_t c; CREATE VIEW "committee" AS SELECT *, @@ -1624,6 +1574,51 @@ FROM committee_t c; comment on column "committee".user_ids is 'Calculated field: All users which are in a group of a meeting, belonging to the committee or beeing manager of the committee'; +CREATE VIEW "gender" AS SELECT *, +(select array_agg(u.id ORDER BY u.id) from user_t u where u.gender_id = g.id) as user_ids +FROM gender_t g; + + +CREATE VIEW "group" AS SELECT *, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, +(select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, +(select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, +(select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_access_group_ids, +(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, +(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, +(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_write_group_ids_group_t n where n.group_id = g.id) as write_chat_group_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids +FROM group_t g; + +comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; + +CREATE VIEW "history_entry" AS SELECT * FROM history_entry_t h; + + +CREATE VIEW "history_position" AS SELECT *, +(select array_agg(he.id ORDER BY he.id) from history_entry_t he where he.position_id = h.id) as entry_ids +FROM history_position_t h; + + +CREATE VIEW "import_preview" AS SELECT * FROM import_preview_t i; + + +CREATE VIEW "list_of_speakers" AS SELECT *, +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, +(select array_agg(s.id ORDER BY s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids +FROM list_of_speakers_t l; + + +CREATE VIEW "mediafile" AS SELECT *, +(select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, +(select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids +FROM mediafile_t m; + + CREATE VIEW "meeting" AS SELECT *, (select array_agg(g.id ORDER BY g.id) from group_t g where g.used_as_motion_poll_default_id = m.id) as motion_poll_default_group_ids, (select array_agg(p.id ORDER BY p.id) from poll_candidate_list_t p where p.meeting_id = m.id) as poll_candidate_list_ids, @@ -1700,70 +1695,45 @@ comment on column "meeting_t".is_active_in_organization_id is 'Backrelation and comment on column "meeting_t".is_archived_in_organization_id is 'Backrelation and boolean flag at once'; comment on column "meeting".user_ids is 'Calculated. All user ids from all users assigned to groups of this meeting.'; -CREATE VIEW "structure_level" AS SELECT *, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, -(select array_agg(sl.id ORDER BY sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids -FROM structure_level_t s; - - -CREATE VIEW "group" AS SELECT *, -(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_group_meeting_user_ids_meeting_user_t n where n.group_id = g.id) as meeting_user_ids, -(select m.id from meeting_t m where m.default_group_id = g.id) as default_group_for_meeting_id, -(select m.id from meeting_t m where m.admin_group_id = g.id) as admin_group_for_meeting_id, -(select m.id from meeting_t m where m.anonymous_group_id = g.id) as anonymous_group_for_meeting_id, -(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_access_group_ids, -(select array_agg(n.meeting_mediafile_id ORDER BY n.meeting_mediafile_id) from nm_group_mmiagi_meeting_mediafile_t n where n.group_id = g.id) as meeting_mediafile_inherited_access_group_ids, -(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_read_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as read_comment_section_ids, -(select array_agg(n.motion_comment_section_id ORDER BY n.motion_comment_section_id) from nm_group_write_comment_section_ids_motion_comment_section_t n where n.group_id = g.id) as write_comment_section_ids, -(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_read_group_ids_group_t n where n.group_id = g.id) as read_chat_group_ids, -(select array_agg(n.chat_group_id ORDER BY n.chat_group_id) from nm_chat_group_write_group_ids_group_t n where n.group_id = g.id) as write_chat_group_ids, -(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_group_poll_ids_poll_t n where n.group_id = g.id) as poll_ids -FROM group_t g; - -comment on column "group".meeting_mediafile_inherited_access_group_ids is 'Calculated field.'; - -CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; - - -CREATE VIEW "tag" AS SELECT *, -(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids -FROM tag_t t; - - -CREATE VIEW "agenda_item" AS SELECT *, -(select array_agg(ai.id ORDER BY ai.id) from agenda_item_t ai where ai.parent_id = a.id) as child_ids, -(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_agenda_item_id = a.id) as tag_ids, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_agenda_item_id = a.id) as projection_ids -FROM agenda_item_t a; - - -CREATE VIEW "list_of_speakers" AS SELECT *, -(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.list_of_speakers_id = l.id) as speaker_ids, -(select array_agg(s.id ORDER BY s.id) from structure_level_list_of_speakers_t s where s.list_of_speakers_id = l.id) as structure_level_list_of_speakers_ids, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_list_of_speakers_id = l.id) as projection_ids -FROM list_of_speakers_t l; - - -CREATE VIEW "structure_level_list_of_speakers" AS SELECT *, -(select array_agg(st.id ORDER BY st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids -FROM structure_level_list_of_speakers_t s; - - -CREATE VIEW "point_of_order_category" AS SELECT *, -(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids -FROM point_of_order_category_t p; - - -CREATE VIEW "speaker" AS SELECT * FROM speaker_t s; +CREATE VIEW "meeting_mediafile" AS SELECT *, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmiagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as access_group_ids, +(select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, +(select array_agg(g.attachment_id ORDER BY g.attachment_id) from gm_meeting_mediafile_attachment_ids_t g where g.meeting_mediafile_id = m.id) as attachment_ids, +(select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, +(select m1.id from meeting_t m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, +(select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id +FROM meeting_mediafile_t m; +comment on column "meeting_mediafile".inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; -CREATE VIEW "topic" AS SELECT *, -(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, -(select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, -(select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, -(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_topic_id = t.id) as projection_ids -FROM topic_t t; +CREATE VIEW "meeting_user" AS SELECT *, +(select array_agg(p.id ORDER BY p.id) from personal_note_t p where p.meeting_user_id = m.id) as personal_note_ids, +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.meeting_user_id = m.id) as speaker_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_supporter_t ms where ms.meeting_user_id = m.id) as motion_supporter_ids, +(select array_agg(me.id ORDER BY me.id) from motion_editor_t me where me.meeting_user_id = m.id) as motion_editor_ids, +(select array_agg(mw.id ORDER BY mw.id) from motion_working_group_speaker_t mw where mw.meeting_user_id = m.id) as motion_working_group_speaker_ids, +(select array_agg(ms.id ORDER BY ms.id) from motion_submitter_t ms where ms.meeting_user_id = m.id) as motion_submitter_ids, +(select array_agg(a.id ORDER BY a.id) from assignment_candidate_t a where a.meeting_user_id = m.id) as assignment_candidate_ids, +(select array_agg(mu.id ORDER BY mu.id) from meeting_user_t mu where mu.vote_delegated_to_id = m.id) as vote_delegations_from_ids, +(select array_agg(c.id ORDER BY c.id) from chat_message_t c where c.meeting_user_id = m.id) as chat_message_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_meeting_user_ids_meeting_user_t n where n.meeting_user_id = m.id) as group_ids, +(select array_agg(n.structure_level_id ORDER BY n.structure_level_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.meeting_user_id = m.id) as structure_level_ids +FROM meeting_user_t m; CREATE VIEW "motion" AS SELECT *, @@ -1795,16 +1765,21 @@ CREATE VIEW "motion" AS SELECT *, FROM motion_t m; -CREATE VIEW "motion_submitter" AS SELECT * FROM motion_submitter_t m; - - -CREATE VIEW "motion_supporter" AS SELECT * FROM motion_supporter_t m; +CREATE VIEW "motion_block" AS SELECT *, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, +(select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids +FROM motion_block_t m; -CREATE VIEW "motion_editor" AS SELECT * FROM motion_editor_t m; +CREATE VIEW "motion_category" AS SELECT *, +(select array_agg(mc.id ORDER BY mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, +(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids +FROM motion_category_t m; -CREATE VIEW "motion_working_group_speaker" AS SELECT * FROM motion_working_group_speaker_t m; +CREATE VIEW "motion_change_recommendation" AS SELECT * FROM motion_change_recommendation_t m; CREATE VIEW "motion_comment" AS SELECT * FROM motion_comment_t m; @@ -1817,21 +1792,7 @@ CREATE VIEW "motion_comment_section" AS SELECT *, FROM motion_comment_section_t m; -CREATE VIEW "motion_category" AS SELECT *, -(select array_agg(mc.id ORDER BY mc.id) from motion_category_t mc where mc.parent_id = m.id) as child_ids, -(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.category_id = m.id) as motion_ids -FROM motion_category_t m; - - -CREATE VIEW "motion_block" AS SELECT *, -(select array_agg(mt.id ORDER BY mt.id) from motion_t mt where mt.block_id = m.id) as motion_ids, -(select a.id from agenda_item_t a where a.content_object_id_motion_block_id = m.id) as agenda_item_id, -(select l.id from list_of_speakers_t l where l.content_object_id_motion_block_id = m.id) as list_of_speakers_id, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_motion_block_id = m.id) as projection_ids -FROM motion_block_t m; - - -CREATE VIEW "motion_change_recommendation" AS SELECT * FROM motion_change_recommendation_t m; +CREATE VIEW "motion_editor" AS SELECT * FROM motion_editor_t m; CREATE VIEW "motion_state" AS SELECT *, @@ -1844,6 +1805,12 @@ CREATE VIEW "motion_state" AS SELECT *, FROM motion_state_t m; +CREATE VIEW "motion_submitter" AS SELECT * FROM motion_submitter_t m; + + +CREATE VIEW "motion_supporter" AS SELECT * FROM motion_supporter_t m; + + CREATE VIEW "motion_workflow" AS SELECT *, (select array_agg(ms.id ORDER BY ms.id) from motion_state_t ms where ms.workflow_id = m.id) as state_ids, (select m1.id from meeting_t m1 where m1.motions_default_workflow_id = m.id) as default_workflow_meeting_id, @@ -1851,12 +1818,7 @@ CREATE VIEW "motion_workflow" AS SELECT *, FROM motion_workflow_t m; -CREATE VIEW "poll" AS SELECT *, -(select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, -(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, -(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids -FROM poll_t p; +CREATE VIEW "motion_working_group_speaker" AS SELECT * FROM motion_working_group_speaker_t m; CREATE VIEW "option" AS SELECT *, @@ -1864,64 +1826,52 @@ CREATE VIEW "option" AS SELECT *, FROM option_t o; -CREATE VIEW "vote" AS SELECT * FROM vote_t v; +CREATE VIEW "organization" AS SELECT *, +(select array_agg(g.id ORDER BY g.id) from gender_t g where g.organization_id = o.id) as gender_ids, +(select array_agg(c.id ORDER BY c.id) from committee_t c where c.organization_id = o.id) as committee_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_active_in_organization_id = o.id) as active_meeting_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.is_archived_in_organization_id = o.id) as archived_meeting_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_t m where m.template_for_organization_id = o.id) as template_meeting_ids, +(select array_agg(ot.id ORDER BY ot.id) from organization_tag_t ot where ot.organization_id = o.id) as organization_tag_ids, +(select array_agg(t.id ORDER BY t.id) from theme_t t where t.organization_id = o.id) as theme_ids, +(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.owner_id_organization_id = o.id) as mediafile_ids, +(select array_agg(m.id ORDER BY m.id) from mediafile_t m where m.published_to_meetings_in_organization_id = o.id) as published_mediafile_ids, +(select array_agg(u.id ORDER BY u.id) from user_t u where u.organization_id = o.id) as user_ids +FROM organization_t o; -CREATE VIEW "assignment" AS SELECT *, -(select array_agg(ac.id ORDER BY ac.id) from assignment_candidate_t ac where ac.assignment_id = a.id) as candidate_ids, -(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_assignment_id = a.id) as poll_ids, -(select ai.id from agenda_item_t ai where ai.content_object_id_assignment_id = a.id) as agenda_item_id, -(select l.id from list_of_speakers_t l where l.content_object_id_assignment_id = a.id) as list_of_speakers_id, -(select array_agg(g.tag_id ORDER BY g.tag_id) from gm_tag_tagged_ids_t g where g.tagged_id_assignment_id = a.id) as tag_ids, -(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_assignment_id = a.id) as attachment_meeting_mediafile_ids, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_assignment_id = a.id) as projection_ids, -(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_assignment_id = a.id) as history_entry_ids -FROM assignment_t a; +CREATE VIEW "organization_tag" AS SELECT *, +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_organization_tag_tagged_ids_t g where g.organization_tag_id = o.id) as tagged_ids +FROM organization_tag_t o; -CREATE VIEW "assignment_candidate" AS SELECT * FROM assignment_candidate_t a; +CREATE VIEW "personal_note" AS SELECT * FROM personal_note_t p; -CREATE VIEW "poll_candidate_list" AS SELECT *, -(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, -(select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id -FROM poll_candidate_list_t p; +CREATE VIEW "point_of_order_category" AS SELECT *, +(select array_agg(s.id ORDER BY s.id) from speaker_t s where s.point_of_order_category_id = p.id) as speaker_ids +FROM point_of_order_category_t p; + + +CREATE VIEW "poll" AS SELECT *, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.poll_id = p.id) as option_ids, +(select array_agg(n.user_id ORDER BY n.user_id) from nm_poll_voted_ids_user_t n where n.poll_id = p.id) as voted_ids, +(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_poll_ids_poll_t n where n.poll_id = p.id) as entitled_group_ids, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_poll_id = p.id) as projection_ids +FROM poll_t p; CREATE VIEW "poll_candidate" AS SELECT * FROM poll_candidate_t p; -CREATE VIEW "mediafile" AS SELECT *, -(select array_agg(mt.id ORDER BY mt.id) from mediafile_t mt where mt.parent_id = m.id) as child_ids, -(select array_agg(mm.id ORDER BY mm.id) from meeting_mediafile_t mm where mm.mediafile_id = m.id) as meeting_mediafile_ids -FROM mediafile_t m; +CREATE VIEW "poll_candidate_list" AS SELECT *, +(select array_agg(pc.id ORDER BY pc.id) from poll_candidate_t pc where pc.poll_candidate_list_id = p.id) as poll_candidate_ids, +(select o.id from option_t o where o.content_object_id_poll_candidate_list_id = p.id) as option_id +FROM poll_candidate_list_t p; -CREATE VIEW "meeting_mediafile" AS SELECT *, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmiagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as inherited_access_group_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_group_mmagi_meeting_mediafile_t n where n.meeting_mediafile_id = m.id) as access_group_ids, -(select l.id from list_of_speakers_t l where l.content_object_id_meeting_mediafile_id = m.id) as list_of_speakers_id, -(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_meeting_mediafile_id = m.id) as projection_ids, -(select array_agg(g.attachment_id ORDER BY g.attachment_id) from gm_meeting_mediafile_attachment_ids_t g where g.meeting_mediafile_id = m.id) as attachment_ids, -(select m1.id from meeting_t m1 where m1.logo_projector_main_id = m.id) as used_as_logo_projector_main_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_projector_header_id = m.id) as used_as_logo_projector_header_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_web_header_id = m.id) as used_as_logo_web_header_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_pdf_header_l_id = m.id) as used_as_logo_pdf_header_l_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_pdf_header_r_id = m.id) as used_as_logo_pdf_header_r_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_pdf_footer_l_id = m.id) as used_as_logo_pdf_footer_l_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_pdf_footer_r_id = m.id) as used_as_logo_pdf_footer_r_in_meeting_id, -(select m1.id from meeting_t m1 where m1.logo_pdf_ballot_paper_id = m.id) as used_as_logo_pdf_ballot_paper_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_regular_id = m.id) as used_as_font_regular_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_italic_id = m.id) as used_as_font_italic_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_bold_id = m.id) as used_as_font_bold_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_bold_italic_id = m.id) as used_as_font_bold_italic_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_monospace_id = m.id) as used_as_font_monospace_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_chyron_speaker_name_id = m.id) as used_as_font_chyron_speaker_name_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_projector_h1_id = m.id) as used_as_font_projector_h1_in_meeting_id, -(select m1.id from meeting_t m1 where m1.font_projector_h2_id = m.id) as used_as_font_projector_h2_in_meeting_id -FROM meeting_mediafile_t m; +CREATE VIEW "projection" AS SELECT * FROM projection_t p; -comment on column "meeting_mediafile".inherited_access_group_ids is 'Calculated in actions. Shows what access group permissions are actually relevant. Calculated as the intersection of this meeting_mediafiles access_group_ids and the related mediafiles potential parent mediafiles inherited_access_group_ids. If the parent has no meeting_mediafile for this meeting, its inherited access group is assumed to be the meetings admin group. If there is no parent, the inherited_access_group_ids is equal to the access_group_ids. If the access_group_ids are empty, the interpretations is that every group has access rights, therefore the parent inherited_access_group_ids are used as-is.'; CREATE VIEW "projector" AS SELECT *, (select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.current_projector_id = p.id) as current_projection_ids, @@ -1931,7 +1881,11 @@ CREATE VIEW "projector" AS SELECT *, FROM projector_t p; -CREATE VIEW "projection" AS SELECT * FROM projection_t p; +CREATE VIEW "projector_countdown" AS SELECT *, +(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, +(select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, +(select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id +FROM projector_countdown_t p; CREATE VIEW "projector_message" AS SELECT *, @@ -1939,50 +1893,139 @@ CREATE VIEW "projector_message" AS SELECT *, FROM projector_message_t p; -CREATE VIEW "projector_countdown" AS SELECT *, -(select array_agg(pt.id ORDER BY pt.id) from projection_t pt where pt.content_object_id_projector_countdown_id = p.id) as projection_ids, -(select m.id from meeting_t m where m.list_of_speakers_countdown_id = p.id) as used_as_list_of_speakers_countdown_meeting_id, -(select m.id from meeting_t m where m.poll_countdown_id = p.id) as used_as_poll_countdown_meeting_id -FROM projector_countdown_t p; +CREATE VIEW "speaker" AS SELECT * FROM speaker_t s; -CREATE VIEW "chat_group" AS SELECT *, -(select array_agg(cm.id ORDER BY cm.id) from chat_message_t cm where cm.chat_group_id = c.id) as chat_message_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_read_group_ids_group_t n where n.chat_group_id = c.id) as read_group_ids, -(select array_agg(n.group_id ORDER BY n.group_id) from nm_chat_group_write_group_ids_group_t n where n.chat_group_id = c.id) as write_group_ids -FROM chat_group_t c; +CREATE VIEW "structure_level" AS SELECT *, +(select array_agg(n.meeting_user_id ORDER BY n.meeting_user_id) from nm_meeting_user_structure_level_ids_structure_level_t n where n.structure_level_id = s.id) as meeting_user_ids, +(select array_agg(sl.id ORDER BY sl.id) from structure_level_list_of_speakers_t sl where sl.structure_level_id = s.id) as structure_level_list_of_speakers_ids +FROM structure_level_t s; -CREATE VIEW "chat_message" AS SELECT * FROM chat_message_t c; +CREATE VIEW "structure_level_list_of_speakers" AS SELECT *, +(select array_agg(st.id ORDER BY st.id) from speaker_t st where st.structure_level_list_of_speakers_id = s.id) as speaker_ids +FROM structure_level_list_of_speakers_t s; -CREATE VIEW "action_worker" AS SELECT * FROM action_worker_t a; +CREATE VIEW "tag" AS SELECT *, +(select array_agg(g.tagged_id ORDER BY g.tagged_id) from gm_tag_tagged_ids_t g where g.tag_id = t.id) as tagged_ids +FROM tag_t t; -CREATE VIEW "import_preview" AS SELECT * FROM import_preview_t i; +CREATE VIEW "theme" AS SELECT *, +(select o.id from organization_t o where o.theme_id = t.id) as theme_for_organization_id +FROM theme_t t; + + +CREATE VIEW "topic" AS SELECT *, +(select array_agg(g.meeting_mediafile_id ORDER BY g.meeting_mediafile_id) from gm_meeting_mediafile_attachment_ids_t g where g.attachment_id_topic_id = t.id) as attachment_meeting_mediafile_ids, +(select a.id from agenda_item_t a where a.content_object_id_topic_id = t.id) as agenda_item_id, +(select l.id from list_of_speakers_t l where l.content_object_id_topic_id = t.id) as list_of_speakers_id, +(select array_agg(p.id ORDER BY p.id) from poll_t p where p.content_object_id_topic_id = t.id) as poll_ids, +(select array_agg(p.id ORDER BY p.id) from projection_t p where p.content_object_id_topic_id = t.id) as projection_ids +FROM topic_t t; + + +CREATE VIEW "user" AS SELECT *, +(select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, +( + SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + FROM ( + -- Select committee_ids from meetings the user is part of + SELECT m.committee_id + FROM meeting_user_t AS mu + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id + + UNION + + -- Select committee_ids from committee managers + SELECT cmu.committee_id + FROM nm_committee_manager_ids_user_t cmu + WHERE cmu.user_id = u.id + + UNION + + -- Select home_committee_id from user + SELECT u.home_committee_id + WHERE u.home_committee_id IS NOT NULL + ) _ +) AS committee_ids +, +(select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, +(select array_agg(m.id ORDER BY m.id) from meeting_user_t m where m.user_id = u.id) as meeting_user_ids, +(select array_agg(n.poll_id ORDER BY n.poll_id) from nm_poll_voted_ids_user_t n where n.user_id = u.id) as poll_voted_ids, +(select array_agg(o.id ORDER BY o.id) from option_t o where o.content_object_id_user_id = u.id) as option_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.user_id = u.id) as vote_ids, +(select array_agg(v.id ORDER BY v.id) from vote_t v where v.delegated_user_id = u.id) as delegated_vote_ids, +(select array_agg(p.id ORDER BY p.id) from poll_candidate_t p where p.user_id = u.id) as poll_candidate_ids, +(select array_agg(h.id ORDER BY h.id) from history_position_t h where h.user_id = u.id) as history_position_ids, +(select array_agg(h.id ORDER BY h.id) from history_entry_t h where h.model_id_user_id = u.id) as history_entry_ids, +( + SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) + FROM meeting_user_t mu + INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id + WHERE mu.user_id = u.id +) AS meeting_ids + +FROM user_t u; + +comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; +comment on column "user".meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; + +CREATE VIEW "vote" AS SELECT * FROM vote_t v; + + + +-- Alter table relations +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -CREATE VIEW "history_position" AS SELECT *, -(select array_agg(he.id ORDER BY he.id) from history_entry_t he where he.position_id = h.id) as entry_ids -FROM history_position_t h; +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -CREATE VIEW "history_entry" AS SELECT * FROM history_entry_t h; +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; +ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE committee_t ADD FOREIGN KEY(parent_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; --- Alter table relations -ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(position_id) REFERENCES history_position_t(id) INITIALLY DEFERRED; +ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE user_t ADD FOREIGN KEY(gender_id) REFERENCES gender_t(id) INITIALLY DEFERRED; -ALTER TABLE user_t ADD FOREIGN KEY(home_committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +ALTER TABLE history_position_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE committee_t ADD FOREIGN KEY(parent_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; @@ -2013,47 +2056,12 @@ ALTER TABLE meeting_t ADD FOREIGN KEY(default_group_id) REFERENCES group_t(id) I ALTER TABLE meeting_t ADD FOREIGN KEY(admin_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; ALTER TABLE meeting_t ADD FOREIGN KEY(anonymous_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; -ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE group_t ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE group_t ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE group_t ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE group_t ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; -ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id) INITIALLY DEFERRED; -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; -ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; -ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id) INITIALLY DEFERRED; -ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id) INITIALLY DEFERRED; -ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(lead_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(sort_parent_id) REFERENCES motion_t(id) INITIALLY DEFERRED; @@ -2065,21 +2073,13 @@ ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(i ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_supporter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE motion_comment_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; ALTER TABLE motion_comment_t ADD FOREIGN KEY(section_id) REFERENCES motion_comment_section_t(id) INITIALLY DEFERRED; @@ -2087,26 +2087,28 @@ ALTER TABLE motion_comment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id ALTER TABLE motion_comment_section_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; - -ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE motion_state_t ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_state_t ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; ALTER TABLE option_t ADD FOREIGN KEY(used_as_global_option_in_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; @@ -2115,30 +2117,41 @@ ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_ ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; -ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; @@ -2156,62 +2169,56 @@ ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_pol ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; -ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; ALTER TABLE projector_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id) INITIALLY DEFERRED; +ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; -ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; -ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE history_position_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; -ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; -ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; -ALTER TABLE history_entry_t ADD FOREIGN KEY(position_id) REFERENCES history_position_t(id) INITIALLY DEFERRED; -ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; + +ALTER TABLE user_t ADD FOREIGN KEY(gender_id) REFERENCES gender_t(id) INITIALLY DEFERRED; +ALTER TABLE user_t ADD FOREIGN KEY(home_committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; + +ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; -- Create triggers generating partitioned sequences +-- definition trigger generate partitioned sequence number for assignment_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_assignment_sequential_number BEFORE INSERT ON assignment_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('assignment_t', 'sequential_number', 'meeting_id'); + + -- definition trigger generate partitioned sequence number for list_of_speakers_t.sequential_number partitioned by meeting_id CREATE TRIGGER tr_generate_sequence_list_of_speakers_sequential_number BEFORE INSERT ON list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION generate_sequence('list_of_speakers_t', 'sequential_number', 'meeting_id'); --- definition trigger generate partitioned sequence number for topic_t.sequential_number partitioned by meeting_id -CREATE TRIGGER tr_generate_sequence_topic_sequential_number BEFORE INSERT ON topic_t -FOR EACH ROW EXECUTE FUNCTION generate_sequence('topic_t', 'sequential_number', 'meeting_id'); - - -- definition trigger generate partitioned sequence number for motion_t.sequential_number partitioned by meeting_id CREATE TRIGGER tr_generate_sequence_motion_sequential_number BEFORE INSERT ON motion_t FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_t', 'sequential_number', 'meeting_id'); --- definition trigger generate partitioned sequence number for motion_comment_section_t.sequential_number partitioned by meeting_id -CREATE TRIGGER tr_generate_sequence_motion_comment_section_sequential_number BEFORE INSERT ON motion_comment_section_t -FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_comment_section_t', 'sequential_number', 'meeting_id'); +-- definition trigger generate partitioned sequence number for motion_block_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_block_sequential_number BEFORE INSERT ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_block_t', 'sequential_number', 'meeting_id'); -- definition trigger generate partitioned sequence number for motion_category_t.sequential_number partitioned by meeting_id @@ -2219,9 +2226,9 @@ CREATE TRIGGER tr_generate_sequence_motion_category_sequential_number BEFORE INS FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_category_t', 'sequential_number', 'meeting_id'); --- definition trigger generate partitioned sequence number for motion_block_t.sequential_number partitioned by meeting_id -CREATE TRIGGER tr_generate_sequence_motion_block_sequential_number BEFORE INSERT ON motion_block_t -FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_block_t', 'sequential_number', 'meeting_id'); +-- definition trigger generate partitioned sequence number for motion_comment_section_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_motion_comment_section_sequential_number BEFORE INSERT ON motion_comment_section_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('motion_comment_section_t', 'sequential_number', 'meeting_id'); -- definition trigger generate partitioned sequence number for motion_workflow_t.sequential_number partitioned by meeting_id @@ -2234,32 +2241,25 @@ CREATE TRIGGER tr_generate_sequence_poll_sequential_number BEFORE INSERT ON poll FOR EACH ROW EXECUTE FUNCTION generate_sequence('poll_t', 'sequential_number', 'meeting_id'); --- definition trigger generate partitioned sequence number for assignment_t.sequential_number partitioned by meeting_id -CREATE TRIGGER tr_generate_sequence_assignment_sequential_number BEFORE INSERT ON assignment_t -FOR EACH ROW EXECUTE FUNCTION generate_sequence('assignment_t', 'sequential_number', 'meeting_id'); - - -- definition trigger generate partitioned sequence number for projector_t.sequential_number partitioned by meeting_id CREATE TRIGGER tr_generate_sequence_projector_sequential_number BEFORE INSERT ON projector_t FOR EACH ROW EXECUTE FUNCTION generate_sequence('projector_t', 'sequential_number', 'meeting_id'); +-- definition trigger generate partitioned sequence number for topic_t.sequential_number partitioned by meeting_id +CREATE TRIGGER tr_generate_sequence_topic_sequential_number BEFORE INSERT ON topic_t +FOR EACH ROW EXECUTE FUNCTION generate_sequence('topic_t', 'sequential_number', 'meeting_id'); --- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships - --- definition trigger not null for topic.agenda_item_id against agenda_item.content_object_id_topic_id -CREATE CONSTRAINT TRIGGER tr_i_topic_agenda_item_id AFTER INSERT ON topic_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', ''); - -CREATE CONSTRAINT TRIGGER tr_ud_topic_agenda_item_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', 'content_object_id_topic_id'); --- definition trigger not null for topic.list_of_speakers_id against list_of_speakers.content_object_id_topic_id -CREATE CONSTRAINT TRIGGER tr_i_topic_list_of_speakers_id AFTER INSERT ON topic_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', ''); -CREATE CONSTRAINT TRIGGER tr_ud_topic_list_of_speakers_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', 'content_object_id_topic_id'); +-- Create triggers checking foreign_id not null for view-relations and no duplicates in 1:1 relationships + +-- definition trigger not null for assignment.list_of_speakers_id against list_of_speakers.content_object_id_assignment_id +CREATE CONSTRAINT TRIGGER tr_i_assignment_list_of_speakers_id AFTER INSERT ON assignment_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_assignment_list_of_speakers_id AFTER UPDATE OF content_object_id_assignment_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'content_object_id_assignment_id'); -- definition trigger not null for motion.list_of_speakers_id against list_of_speakers.content_object_id_motion_id @@ -2278,14 +2278,6 @@ CREATE CONSTRAINT TRIGGER tr_ud_motion_block_list_of_speakers_id AFTER UPDATE OF FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', 'content_object_id_motion_block_id'); --- definition trigger not null for assignment.list_of_speakers_id against list_of_speakers.content_object_id_assignment_id -CREATE CONSTRAINT TRIGGER tr_i_assignment_list_of_speakers_id AFTER INSERT ON assignment_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', ''); - -CREATE CONSTRAINT TRIGGER tr_ud_assignment_list_of_speakers_id AFTER UPDATE OF content_object_id_assignment_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'content_object_id_assignment_id'); - - -- definition trigger not null for poll_candidate_list.option_id against option.content_object_id_poll_candidate_list_id CREATE CONSTRAINT TRIGGER tr_i_poll_candidate_list_option_id AFTER INSERT ON poll_candidate_list_t INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', ''); @@ -2294,6 +2286,21 @@ CREATE CONSTRAINT TRIGGER tr_ud_poll_candidate_list_option_id AFTER UPDATE OF co FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', 'content_object_id_poll_candidate_list_id'); +-- definition trigger not null for topic.agenda_item_id against agenda_item.content_object_id_topic_id +CREATE CONSTRAINT TRIGGER tr_i_topic_agenda_item_id AFTER INSERT ON topic_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_topic_agenda_item_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', 'content_object_id_topic_id'); + +-- definition trigger not null for topic.list_of_speakers_id against list_of_speakers.content_object_id_topic_id +CREATE CONSTRAINT TRIGGER tr_i_topic_list_of_speakers_id AFTER INSERT ON topic_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', ''); + +CREATE CONSTRAINT TRIGGER tr_ud_topic_list_of_speakers_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', 'content_object_id_topic_id'); + + -- Create triggers checking foreign_id not null for relation-lists @@ -2421,65 +2428,83 @@ FOR EACH ROW EXECUTE FUNCTION check_unique_ids_pair('identical_motion_id'); -- Create triggers for notify -CREATE TRIGGER tr_log_organization AFTER INSERT OR UPDATE OR DELETE ON organization_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_t +CREATE TRIGGER tr_log_action_worker AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('action_worker'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON action_worker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_organization_t_theme_id AFTER INSERT OR UPDATE OF theme_id OR DELETE ON organization_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('theme', 'theme_id'); - -CREATE TRIGGER tr_log_user AFTER INSERT OR UPDATE OR DELETE ON user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('user'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON user_t +CREATE TRIGGER tr_log_agenda_item AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('agenda_item'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_user_t_gender_id AFTER INSERT OR UPDATE OF gender_id OR DELETE ON user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('gender', 'gender_id'); -CREATE TRIGGER tr_log_user_t_home_committee_id AFTER INSERT OR UPDATE OF home_committee_id OR DELETE ON user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'home_committee_id'); -CREATE TRIGGER tr_log_meeting_user AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_user'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); -CREATE TRIGGER tr_log_meeting_user_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON meeting_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); -CREATE TRIGGER tr_log_meeting_user_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_user_t +CREATE TRIGGER tr_log_motion_block_content_object_id_motion_block_id AFTER INSERT OR UPDATE OF content_object_id_motion_block_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block','content_object_id_motion_block_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); +CREATE TRIGGER tr_log_agenda_item_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON agenda_item_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('agenda_item', 'parent_id'); +CREATE TRIGGER tr_log_agenda_item_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON agenda_item_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); -CREATE TRIGGER tr_log_nm_meeting_user_structure_level_ids_structure_level_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','structure_level','structure_level_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t +CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_gender AFTER INSERT OR UPDATE OR DELETE ON gender_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('gender'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gender_t +CREATE TRIGGER tr_log_assignment_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_assignment_candidate AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment_candidate'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_organization_tag AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization_tag'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +CREATE TRIGGER tr_log_assignment_candidate_t_assignment_id AFTER INSERT OR UPDATE OF assignment_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment', 'assignment_id'); +CREATE TRIGGER tr_log_assignment_candidate_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_assignment_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_candidate_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_chat_group AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_group'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_tagged_id_committee_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_committee_id OR DELETE ON gm_organization_tag_tagged_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','committee','tagged_id_committee_id'); +CREATE TRIGGER tr_log_nm_chat_group_read_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_tagged_id_meeting_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_meeting_id OR DELETE ON gm_organization_tag_tagged_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','meeting','tagged_id_meeting_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_organization_tag_tagged_ids_t +CREATE TRIGGER tr_log_nm_chat_group_write_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_chat_group_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_group_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_theme AFTER INSERT OR UPDATE OR DELETE ON theme_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('theme'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON theme_t +CREATE TRIGGER tr_log_chat_message AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_message'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_message_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_chat_message_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_chat_message_t_chat_group_id AFTER INSERT OR UPDATE OF chat_group_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group', 'chat_group_id'); +CREATE TRIGGER tr_log_chat_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + CREATE TRIGGER tr_log_committee AFTER INSERT OR UPDATE OR DELETE ON committee_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('committee'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON committee_t @@ -2505,81 +2530,11 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee','forward_t CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_committee_forward_to_committee_ids_committee_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_meeting AFTER INSERT OR UPDATE OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_meeting_t_is_active_in_organization_id AFTER INSERT OR UPDATE OF is_active_in_organization_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_active_in_organization_id'); -CREATE TRIGGER tr_log_meeting_t_is_archived_in_organization_id AFTER INSERT OR UPDATE OF is_archived_in_organization_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_archived_in_organization_id'); -CREATE TRIGGER tr_log_meeting_t_template_for_organization_id AFTER INSERT OR UPDATE OF template_for_organization_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'template_for_organization_id'); -CREATE TRIGGER tr_log_meeting_t_motions_default_workflow_id AFTER INSERT OR UPDATE OF motions_default_workflow_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_workflow_id'); -CREATE TRIGGER tr_log_meeting_t_motions_default_amendment_workflow_id AFTER INSERT OR UPDATE OF motions_default_amendment_workflow_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_amendment_workflow_id'); -CREATE TRIGGER tr_log_meeting_t_logo_projector_main_id AFTER INSERT OR UPDATE OF logo_projector_main_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_main_id'); -CREATE TRIGGER tr_log_meeting_t_logo_projector_header_id AFTER INSERT OR UPDATE OF logo_projector_header_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_header_id'); -CREATE TRIGGER tr_log_meeting_t_logo_web_header_id AFTER INSERT OR UPDATE OF logo_web_header_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_web_header_id'); -CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_l_id AFTER INSERT OR UPDATE OF logo_pdf_header_l_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_l_id'); -CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_r_id AFTER INSERT OR UPDATE OF logo_pdf_header_r_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_r_id'); -CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_l_id AFTER INSERT OR UPDATE OF logo_pdf_footer_l_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_l_id'); -CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_r_id AFTER INSERT OR UPDATE OF logo_pdf_footer_r_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_r_id'); -CREATE TRIGGER tr_log_meeting_t_logo_pdf_ballot_paper_id AFTER INSERT OR UPDATE OF logo_pdf_ballot_paper_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_ballot_paper_id'); -CREATE TRIGGER tr_log_meeting_t_font_regular_id AFTER INSERT OR UPDATE OF font_regular_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_regular_id'); -CREATE TRIGGER tr_log_meeting_t_font_italic_id AFTER INSERT OR UPDATE OF font_italic_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_italic_id'); -CREATE TRIGGER tr_log_meeting_t_font_bold_id AFTER INSERT OR UPDATE OF font_bold_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_id'); -CREATE TRIGGER tr_log_meeting_t_font_bold_italic_id AFTER INSERT OR UPDATE OF font_bold_italic_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_italic_id'); -CREATE TRIGGER tr_log_meeting_t_font_monospace_id AFTER INSERT OR UPDATE OF font_monospace_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_monospace_id'); -CREATE TRIGGER tr_log_meeting_t_font_chyron_speaker_name_id AFTER INSERT OR UPDATE OF font_chyron_speaker_name_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_chyron_speaker_name_id'); -CREATE TRIGGER tr_log_meeting_t_font_projector_h1_id AFTER INSERT OR UPDATE OF font_projector_h1_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h1_id'); -CREATE TRIGGER tr_log_meeting_t_font_projector_h2_id AFTER INSERT OR UPDATE OF font_projector_h2_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h2_id'); -CREATE TRIGGER tr_log_meeting_t_committee_id AFTER INSERT OR UPDATE OF committee_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'committee_id'); - -CREATE TRIGGER tr_log_nm_meeting_present_user_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','meeting_id','user','user_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_meeting_t_reference_projector_id AFTER INSERT OR UPDATE OF reference_projector_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'reference_projector_id'); -CREATE TRIGGER tr_log_meeting_t_list_of_speakers_countdown_id AFTER INSERT OR UPDATE OF list_of_speakers_countdown_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'list_of_speakers_countdown_id'); -CREATE TRIGGER tr_log_meeting_t_poll_countdown_id AFTER INSERT OR UPDATE OF poll_countdown_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'poll_countdown_id'); -CREATE TRIGGER tr_log_meeting_t_default_group_id AFTER INSERT OR UPDATE OF default_group_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'default_group_id'); -CREATE TRIGGER tr_log_meeting_t_admin_group_id AFTER INSERT OR UPDATE OF admin_group_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'admin_group_id'); -CREATE TRIGGER tr_log_meeting_t_anonymous_group_id AFTER INSERT OR UPDATE OF anonymous_group_id OR DELETE ON meeting_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'anonymous_group_id'); - -CREATE TRIGGER tr_log_structure_level AFTER INSERT OR UPDATE OR DELETE ON structure_level_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +CREATE TRIGGER tr_log_gender AFTER INSERT OR UPDATE OR DELETE ON gender_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('gender'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gender_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_structure_level_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - CREATE TRIGGER tr_log_group AFTER INSERT OR UPDATE OR DELETE ON group_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('group'); CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON group_t @@ -2626,59 +2581,37 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_po CREATE TRIGGER tr_log_group_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON group_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_personal_note AFTER INSERT OR UPDATE OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('personal_note'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON personal_note_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_personal_note_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); - -CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); -CREATE TRIGGER tr_log_personal_note_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON personal_note_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_tag AFTER INSERT OR UPDATE OR DELETE ON tag_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('tag'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON tag_t +CREATE TRIGGER tr_log_history_entry AFTER INSERT OR UPDATE OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_entry'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_entry_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_tagged_id_agenda_item_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_agenda_item_id OR DELETE ON gm_tag_tagged_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','agenda_item','tagged_id_agenda_item_id'); +CREATE TRIGGER tr_log_user_model_id_user_id AFTER INSERT OR UPDATE OF model_id_user_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user','model_id_user_id'); -CREATE TRIGGER tr_log_tagged_id_assignment_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_assignment_id OR DELETE ON gm_tag_tagged_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','assignment','tagged_id_assignment_id'); +CREATE TRIGGER tr_log_motion_model_id_motion_id AFTER INSERT OR UPDATE OF model_id_motion_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','model_id_motion_id'); -CREATE TRIGGER tr_log_tagged_id_motion_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_motion_id OR DELETE ON gm_tag_tagged_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','motion','tagged_id_motion_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_tag_tagged_ids_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_tag_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON tag_t +CREATE TRIGGER tr_log_assignment_model_id_assignment_id AFTER INSERT OR UPDATE OF model_id_assignment_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','model_id_assignment_id'); +CREATE TRIGGER tr_log_history_entry_t_position_id AFTER INSERT OR UPDATE OF position_id OR DELETE ON history_entry_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('history_position', 'position_id'); +CREATE TRIGGER tr_log_history_entry_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON history_entry_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_agenda_item AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('agenda_item'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON agenda_item_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - - -CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); - -CREATE TRIGGER tr_log_motion_block_content_object_id_motion_block_id AFTER INSERT OR UPDATE OF content_object_id_motion_block_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block','content_object_id_motion_block_id'); +CREATE TRIGGER tr_log_history_position AFTER INSERT OR UPDATE OR DELETE ON history_position_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_position'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_position_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); +CREATE TRIGGER tr_log_history_position_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON history_position_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); -CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); -CREATE TRIGGER tr_log_agenda_item_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('agenda_item', 'parent_id'); -CREATE TRIGGER tr_log_agenda_item_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON agenda_item_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_import_preview AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('import_preview'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER tr_log_list_of_speakers AFTER INSERT OR UPDATE OR DELETE ON list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('list_of_speakers'); @@ -2703,49 +2636,126 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','c CREATE TRIGGER tr_log_list_of_speakers_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_structure_level_list_of_speakers AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level_list_of_speakers'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +CREATE TRIGGER tr_log_mediafile AFTER INSERT OR UPDATE OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_structure_level_id AFTER INSERT OR UPDATE OF structure_level_id OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level', 'structure_level_id'); -CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); -CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_list_of_speakers_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_mediafile_t_published_to_meetings_in_organization_id AFTER INSERT OR UPDATE OF published_to_meetings_in_organization_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'published_to_meetings_in_organization_id'); +CREATE TRIGGER tr_log_mediafile_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'parent_id'); -CREATE TRIGGER tr_log_point_of_order_category AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('point_of_order_category'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +CREATE TRIGGER tr_log_meeting_owner_id_meeting_id AFTER INSERT OR UPDATE OF owner_id_meeting_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','owner_id_meeting_id'); + +CREATE TRIGGER tr_log_organization_owner_id_organization_id AFTER INSERT OR UPDATE OF owner_id_organization_id OR DELETE ON mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization','owner_id_organization_id'); + +CREATE TRIGGER tr_log_meeting AFTER INSERT OR UPDATE OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_point_of_order_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON point_of_order_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_meeting_t_is_active_in_organization_id AFTER INSERT OR UPDATE OF is_active_in_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_active_in_organization_id'); +CREATE TRIGGER tr_log_meeting_t_is_archived_in_organization_id AFTER INSERT OR UPDATE OF is_archived_in_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'is_archived_in_organization_id'); +CREATE TRIGGER tr_log_meeting_t_template_for_organization_id AFTER INSERT OR UPDATE OF template_for_organization_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'template_for_organization_id'); +CREATE TRIGGER tr_log_meeting_t_motions_default_workflow_id AFTER INSERT OR UPDATE OF motions_default_workflow_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_workflow_id'); +CREATE TRIGGER tr_log_meeting_t_motions_default_amendment_workflow_id AFTER INSERT OR UPDATE OF motions_default_amendment_workflow_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'motions_default_amendment_workflow_id'); +CREATE TRIGGER tr_log_meeting_t_logo_projector_main_id AFTER INSERT OR UPDATE OF logo_projector_main_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_main_id'); +CREATE TRIGGER tr_log_meeting_t_logo_projector_header_id AFTER INSERT OR UPDATE OF logo_projector_header_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_projector_header_id'); +CREATE TRIGGER tr_log_meeting_t_logo_web_header_id AFTER INSERT OR UPDATE OF logo_web_header_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_web_header_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_l_id AFTER INSERT OR UPDATE OF logo_pdf_header_l_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_l_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_header_r_id AFTER INSERT OR UPDATE OF logo_pdf_header_r_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_header_r_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_l_id AFTER INSERT OR UPDATE OF logo_pdf_footer_l_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_l_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_footer_r_id AFTER INSERT OR UPDATE OF logo_pdf_footer_r_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_footer_r_id'); +CREATE TRIGGER tr_log_meeting_t_logo_pdf_ballot_paper_id AFTER INSERT OR UPDATE OF logo_pdf_ballot_paper_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'logo_pdf_ballot_paper_id'); +CREATE TRIGGER tr_log_meeting_t_font_regular_id AFTER INSERT OR UPDATE OF font_regular_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_regular_id'); +CREATE TRIGGER tr_log_meeting_t_font_italic_id AFTER INSERT OR UPDATE OF font_italic_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_italic_id'); +CREATE TRIGGER tr_log_meeting_t_font_bold_id AFTER INSERT OR UPDATE OF font_bold_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_id'); +CREATE TRIGGER tr_log_meeting_t_font_bold_italic_id AFTER INSERT OR UPDATE OF font_bold_italic_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_bold_italic_id'); +CREATE TRIGGER tr_log_meeting_t_font_monospace_id AFTER INSERT OR UPDATE OF font_monospace_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_monospace_id'); +CREATE TRIGGER tr_log_meeting_t_font_chyron_speaker_name_id AFTER INSERT OR UPDATE OF font_chyron_speaker_name_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_chyron_speaker_name_id'); +CREATE TRIGGER tr_log_meeting_t_font_projector_h1_id AFTER INSERT OR UPDATE OF font_projector_h1_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h1_id'); +CREATE TRIGGER tr_log_meeting_t_font_projector_h2_id AFTER INSERT OR UPDATE OF font_projector_h2_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile', 'font_projector_h2_id'); +CREATE TRIGGER tr_log_meeting_t_committee_id AFTER INSERT OR UPDATE OF committee_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'committee_id'); -CREATE TRIGGER tr_log_speaker AFTER INSERT OR UPDATE OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('speaker'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON speaker_t +CREATE TRIGGER tr_log_nm_meeting_present_user_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','meeting_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_present_user_ids_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_meeting_t_reference_projector_id AFTER INSERT OR UPDATE OF reference_projector_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector', 'reference_projector_id'); +CREATE TRIGGER tr_log_meeting_t_list_of_speakers_countdown_id AFTER INSERT OR UPDATE OF list_of_speakers_countdown_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'list_of_speakers_countdown_id'); +CREATE TRIGGER tr_log_meeting_t_poll_countdown_id AFTER INSERT OR UPDATE OF poll_countdown_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', 'poll_countdown_id'); +CREATE TRIGGER tr_log_meeting_t_default_group_id AFTER INSERT OR UPDATE OF default_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'default_group_id'); +CREATE TRIGGER tr_log_meeting_t_admin_group_id AFTER INSERT OR UPDATE OF admin_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'admin_group_id'); +CREATE TRIGGER tr_log_meeting_t_anonymous_group_id AFTER INSERT OR UPDATE OF anonymous_group_id OR DELETE ON meeting_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('group', 'anonymous_group_id'); + +CREATE TRIGGER tr_log_meeting_mediafile AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_mediafile'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_speaker_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); -CREATE TRIGGER tr_log_speaker_t_structure_level_list_of_speakers_id AFTER INSERT OR UPDATE OF structure_level_list_of_speakers_id OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level_list_of_speakers', 'structure_level_list_of_speakers_id'); -CREATE TRIGGER tr_log_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_speaker_t_point_of_order_category_id AFTER INSERT OR UPDATE OF point_of_order_category_id OR DELETE ON speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('point_of_order_category', 'point_of_order_category_id'); -CREATE TRIGGER tr_log_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON speaker_t +CREATE TRIGGER tr_log_meeting_mediafile_t_mediafile_id AFTER INSERT OR UPDATE OF mediafile_id OR DELETE ON meeting_mediafile_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'mediafile_id'); +CREATE TRIGGER tr_log_meeting_mediafile_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_mediafile_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_topic AFTER INSERT OR UPDATE OR DELETE ON topic_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('topic'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON topic_t +CREATE TRIGGER tr_log_attachment_id_motion_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_motion_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','motion','attachment_id_motion_id'); + +CREATE TRIGGER tr_log_attachment_id_topic_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_topic_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','topic','attachment_id_topic_id'); + +CREATE TRIGGER tr_log_attachment_id_assignment_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_assignment_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','assignment','attachment_id_assignment_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_meeting_mediafile_attachment_ids_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_topic_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON topic_t +CREATE TRIGGER tr_log_meeting_user AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_user'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_meeting_user_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_meeting_user_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_user_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_meeting_user_t_vote_delegated_to_id AFTER INSERT OR UPDATE OF vote_delegated_to_id OR DELETE ON meeting_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'vote_delegated_to_id'); + +CREATE TRIGGER tr_log_nm_meeting_user_structure_level_ids_structure_level_t AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user','meeting_user_id','structure_level','structure_level_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_meeting_user_structure_level_ids_structure_level_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); CREATE TRIGGER tr_log_motion AFTER INSERT OR UPDATE OR DELETE ON motion_t FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion'); @@ -2791,52 +2801,32 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_block', 'block CREATE TRIGGER tr_log_motion_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_motion_submitter AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_submitter'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_motion_submitter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_motion_submitter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); -CREATE TRIGGER tr_log_motion_submitter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_submitter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_motion_supporter AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_supporter'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t +CREATE TRIGGER tr_log_motion_block AFTER INSERT OR UPDATE OR DELETE ON motion_block_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_block'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_block_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_motion_supporter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_supporter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_motion_supporter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_supporter_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); -CREATE TRIGGER tr_log_motion_supporter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_supporter_t +CREATE TRIGGER tr_log_motion_block_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_block_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_motion_editor AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +CREATE TRIGGER tr_log_motion_category AFTER INSERT OR UPDATE OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_category'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_motion_editor_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_editor_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_motion_editor_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_editor_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); -CREATE TRIGGER tr_log_motion_editor_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_editor_t +CREATE TRIGGER tr_log_motion_category_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON motion_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_category', 'parent_id'); +CREATE TRIGGER tr_log_motion_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_category_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_motion_working_group_speaker AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_working_group_speaker'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +CREATE TRIGGER tr_log_motion_change_recommendation AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_change_recommendation'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_working_group_speaker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_motion_working_group_speaker_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_working_group_speaker_t +CREATE TRIGGER tr_log_motion_change_recommendation_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_change_recommendation_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); -CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_working_group_speaker_t +CREATE TRIGGER tr_log_motion_change_recommendation_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_change_recommendation_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_motion_comment AFTER INSERT OR UPDATE OR DELETE ON motion_comment_t @@ -2859,32 +2849,16 @@ DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_e CREATE TRIGGER tr_log_motion_comment_section_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_comment_section_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_motion_category AFTER INSERT OR UPDATE OR DELETE ON motion_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_category'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_category_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_motion_category_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON motion_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_category', 'parent_id'); -CREATE TRIGGER tr_log_motion_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_category_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_motion_block AFTER INSERT OR UPDATE OR DELETE ON motion_block_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_block'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_block_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_motion_block_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_block_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_motion_change_recommendation AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_change_recommendation'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_change_recommendation_t +CREATE TRIGGER tr_log_motion_editor AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_editor'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_editor_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_motion_change_recommendation_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_change_recommendation_t +CREATE TRIGGER tr_log_motion_editor_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_editor_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_editor_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_editor_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); -CREATE TRIGGER tr_log_motion_change_recommendation_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_change_recommendation_t +CREATE TRIGGER tr_log_motion_editor_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_editor_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_motion_state AFTER INSERT OR UPDATE OR DELETE ON motion_state_t @@ -2895,13 +2869,37 @@ DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_e CREATE TRIGGER tr_log_motion_state_t_submitter_withdraw_state_id AFTER INSERT OR UPDATE OF submitter_withdraw_state_id OR DELETE ON motion_state_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'submitter_withdraw_state_id'); -CREATE TRIGGER tr_log_nm_motion_state_next_state_ids_motion_state_t AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state','next_state_id','motion_state','previous_state_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t +CREATE TRIGGER tr_log_nm_motion_state_next_state_ids_motion_state_t AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state','next_state_id','motion_state','previous_state_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_motion_state_next_state_ids_motion_state_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_motion_state_t_workflow_id AFTER INSERT OR UPDATE OF workflow_id OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'workflow_id'); +CREATE TRIGGER tr_log_motion_state_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_state_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_submitter AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_submitter'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_submitter_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_motion_submitter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_submitter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_submitter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_submitter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_motion_supporter AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_supporter'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_supporter_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_motion_state_t_workflow_id AFTER INSERT OR UPDATE OF workflow_id OR DELETE ON motion_state_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_workflow', 'workflow_id'); -CREATE TRIGGER tr_log_motion_state_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_state_t + +CREATE TRIGGER tr_log_motion_supporter_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_supporter_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_supporter_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_supporter_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_supporter_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_motion_workflow AFTER INSERT OR UPDATE OR DELETE ON motion_workflow_t @@ -2914,28 +2912,16 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion_state', 'first CREATE TRIGGER tr_log_motion_workflow_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_workflow_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_poll AFTER INSERT OR UPDATE OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_t +CREATE TRIGGER tr_log_motion_working_group_speaker AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('motion_working_group_speaker'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON motion_working_group_speaker_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); - -CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); - -CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); -CREATE TRIGGER tr_log_poll_t_global_option_id AFTER INSERT OR UPDATE OF global_option_id OR DELETE ON poll_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'global_option_id'); - -CREATE TRIGGER tr_log_nm_poll_voted_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','poll_id','user','user_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t +CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_motion_working_group_speaker_t_motion_id AFTER INSERT OR UPDATE OF motion_id OR DELETE ON motion_working_group_speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion', 'motion_id'); +CREATE TRIGGER tr_log_motion_working_group_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON motion_working_group_speaker_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_option AFTER INSERT OR UPDATE OR DELETE ON option_t @@ -2959,46 +2945,71 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll_candidate_list', CREATE TRIGGER tr_log_option_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON option_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_vote AFTER INSERT OR UPDATE OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t +CREATE TRIGGER tr_log_organization AFTER INSERT OR UPDATE OR DELETE ON organization_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_vote_t_option_id AFTER INSERT OR UPDATE OF option_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'option_id'); -CREATE TRIGGER tr_log_vote_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); -CREATE TRIGGER tr_log_vote_t_delegated_user_id AFTER INSERT OR UPDATE OF delegated_user_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'delegated_user_id'); -CREATE TRIGGER tr_log_vote_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON vote_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); +CREATE TRIGGER tr_log_organization_t_theme_id AFTER INSERT OR UPDATE OF theme_id OR DELETE ON organization_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('theme', 'theme_id'); -CREATE TRIGGER tr_log_assignment AFTER INSERT OR UPDATE OR DELETE ON assignment_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_t +CREATE TRIGGER tr_log_organization_tag AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('organization_tag'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON organization_tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_assignment_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_assignment_candidate AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('assignment_candidate'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON assignment_candidate_t +CREATE TRIGGER tr_log_tagged_id_committee_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_committee_id OR DELETE ON gm_organization_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','committee','tagged_id_committee_id'); + +CREATE TRIGGER tr_log_tagged_id_meeting_id_gm_organization_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_meeting_id OR DELETE ON gm_organization_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization_tag','organization_tag_id','meeting','tagged_id_meeting_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_organization_tag_tagged_ids_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_assignment_candidate_t_assignment_id AFTER INSERT OR UPDATE OF assignment_id OR DELETE ON assignment_candidate_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment', 'assignment_id'); -CREATE TRIGGER tr_log_assignment_candidate_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON assignment_candidate_t +CREATE TRIGGER tr_log_personal_note AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('personal_note'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON personal_note_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_personal_note_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON personal_note_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_assignment_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON assignment_candidate_t + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON personal_note_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); +CREATE TRIGGER tr_log_personal_note_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON personal_note_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_poll_candidate_list AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +CREATE TRIGGER tr_log_point_of_order_category AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('point_of_order_category'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON point_of_order_category_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_poll_candidate_list_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_list_t +CREATE TRIGGER tr_log_point_of_order_category_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON point_of_order_category_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_poll AFTER INSERT OR UPDATE OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + + +CREATE TRIGGER tr_log_motion_content_object_id_motion_id AFTER INSERT OR UPDATE OF content_object_id_motion_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','content_object_id_motion_id'); + +CREATE TRIGGER tr_log_assignment_content_object_id_assignment_id AFTER INSERT OR UPDATE OF content_object_id_assignment_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','content_object_id_assignment_id'); + +CREATE TRIGGER tr_log_topic_content_object_id_topic_id AFTER INSERT OR UPDATE OF content_object_id_topic_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('topic','content_object_id_topic_id'); +CREATE TRIGGER tr_log_poll_t_global_option_id AFTER INSERT OR UPDATE OF global_option_id OR DELETE ON poll_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'global_option_id'); + +CREATE TRIGGER tr_log_nm_poll_voted_ids_user_t AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('poll','poll_id','user','user_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_poll_voted_ids_user_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_poll_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_poll_candidate AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_t @@ -3013,77 +3024,12 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); CREATE TRIGGER tr_log_poll_candidate_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_mediafile AFTER INSERT OR UPDATE OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('mediafile'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON mediafile_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_mediafile_t_published_to_meetings_in_organization_id AFTER INSERT OR UPDATE OF published_to_meetings_in_organization_id OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization', 'published_to_meetings_in_organization_id'); -CREATE TRIGGER tr_log_mediafile_t_parent_id AFTER INSERT OR UPDATE OF parent_id OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'parent_id'); - -CREATE TRIGGER tr_log_meeting_owner_id_meeting_id AFTER INSERT OR UPDATE OF owner_id_meeting_id OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting','owner_id_meeting_id'); - -CREATE TRIGGER tr_log_organization_owner_id_organization_id AFTER INSERT OR UPDATE OF owner_id_organization_id OR DELETE ON mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('organization','owner_id_organization_id'); - -CREATE TRIGGER tr_log_meeting_mediafile AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('meeting_mediafile'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON meeting_mediafile_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_meeting_mediafile_t_mediafile_id AFTER INSERT OR UPDATE OF mediafile_id OR DELETE ON meeting_mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('mediafile', 'mediafile_id'); -CREATE TRIGGER tr_log_meeting_mediafile_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON meeting_mediafile_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); - -CREATE TRIGGER tr_log_attachment_id_motion_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_motion_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','motion','attachment_id_motion_id'); - -CREATE TRIGGER tr_log_attachment_id_topic_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_topic_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','topic','attachment_id_topic_id'); - -CREATE TRIGGER tr_log_attachment_id_assignment_id_gm_meeting_mediafile_attachment_ids_t AFTER INSERT OR UPDATE OF attachment_id_assignment_id OR DELETE ON gm_meeting_mediafile_attachment_ids_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_mediafile','meeting_mediafile_id','assignment','attachment_id_assignment_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_meeting_mediafile_attachment_ids_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); - -CREATE TRIGGER tr_log_projector AFTER INSERT OR UPDATE OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_t +CREATE TRIGGER tr_log_poll_candidate_list AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('poll_candidate_list'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON poll_candidate_list_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_agenda_item_li AFTER INSERT OR UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_topic_in_meeti AFTER INSERT OR UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_topic_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_list_of_speake AFTER INSERT OR UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_current_los_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_current_los_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_in_meet AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_amendment_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_amendment_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_block_i AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_block_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_in_ AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_mediafile_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_mediafile_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_message_in_mee AFTER INSERT OR UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_message_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_countdown_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_countdown_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_pol AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_poll_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_poll_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_poll_in_meetin AFTER INSERT OR UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_poll_in_meeting_id'); -CREATE TRIGGER tr_log_projector_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_t +CREATE TRIGGER tr_log_poll_candidate_list_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON poll_candidate_list_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_projection AFTER INSERT OR UPDATE OR DELETE ON projection_t @@ -3133,12 +3079,40 @@ FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('projector_countdown', CREATE TRIGGER tr_log_projection_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projection_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_projector_message AFTER INSERT OR UPDATE OR DELETE ON projector_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_message'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +CREATE TRIGGER tr_log_projector AFTER INSERT OR UPDATE OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_projector_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_message_t +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_agenda_item_li AFTER INSERT OR UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_topic_in_meeti AFTER INSERT OR UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_topic_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_list_of_speake AFTER INSERT OR UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_current_los_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_current_los_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_in_meet AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_amendment_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_amendment_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_block_i AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_block_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_in_ AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_mediafile_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_mediafile_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_message_in_mee AFTER INSERT OR UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_message_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_countdown_in_m AFTER INSERT OR UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_countdown_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_assignment_pol AFTER INSERT OR UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_motion_poll_in AFTER INSERT OR UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_motion_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_used_as_default_projector_for_poll_in_meetin AFTER INSERT OR UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'used_as_default_projector_for_poll_in_meeting_id'); +CREATE TRIGGER tr_log_projector_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); CREATE TRIGGER tr_log_projector_countdown AFTER INSERT OR UPDATE OR DELETE ON projector_countdown_t @@ -3149,71 +3123,104 @@ DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_e CREATE TRIGGER tr_log_projector_countdown_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_countdown_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_chat_group AFTER INSERT OR UPDATE OR DELETE ON chat_group_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_group'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_group_t +CREATE TRIGGER tr_log_projector_message AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('projector_message'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON projector_message_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_projector_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON projector_message_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_speaker AFTER INSERT OR UPDATE OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('speaker'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON speaker_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); + +CREATE TRIGGER tr_log_speaker_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); +CREATE TRIGGER tr_log_speaker_t_structure_level_list_of_speakers_id AFTER INSERT OR UPDATE OF structure_level_list_of_speakers_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level_list_of_speakers', 'structure_level_list_of_speakers_id'); +CREATE TRIGGER tr_log_speaker_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); +CREATE TRIGGER tr_log_speaker_t_point_of_order_category_id AFTER INSERT OR UPDATE OF point_of_order_category_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('point_of_order_category', 'point_of_order_category_id'); +CREATE TRIGGER tr_log_speaker_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON speaker_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); + +CREATE TRIGGER tr_log_structure_level AFTER INSERT OR UPDATE OR DELETE ON structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_structure_level_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_nm_chat_group_read_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_read_group_ids_group_t +CREATE TRIGGER tr_log_structure_level_list_of_speakers AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('structure_level_list_of_speakers'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON structure_level_list_of_speakers_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_nm_chat_group_write_group_ids_group_t AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group','chat_group_id','group','group_id'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON nm_chat_group_write_group_ids_group_t -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_chat_group_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_group_t +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_structure_level_id AFTER INSERT OR UPDATE OF structure_level_id OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('structure_level', 'structure_level_id'); +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_list_of_speakers_id AFTER INSERT OR UPDATE OF list_of_speakers_id OR DELETE ON structure_level_list_of_speakers_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('list_of_speakers', 'list_of_speakers_id'); +CREATE TRIGGER tr_log_structure_level_list_of_speakers_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON structure_level_list_of_speakers_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_chat_message AFTER INSERT OR UPDATE OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('chat_message'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON chat_message_t +CREATE TRIGGER tr_log_tag AFTER INSERT OR UPDATE OR DELETE ON tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('tag'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON tag_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_chat_message_t_meeting_user_id AFTER INSERT OR UPDATE OF meeting_user_id OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting_user', 'meeting_user_id'); -CREATE TRIGGER tr_log_chat_message_t_chat_group_id AFTER INSERT OR UPDATE OF chat_group_id OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('chat_group', 'chat_group_id'); -CREATE TRIGGER tr_log_chat_message_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON chat_message_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_action_worker AFTER INSERT OR UPDATE OR DELETE ON action_worker_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('action_worker'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON action_worker_t +CREATE TRIGGER tr_log_tagged_id_agenda_item_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_agenda_item_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','agenda_item','tagged_id_agenda_item_id'); + +CREATE TRIGGER tr_log_tagged_id_assignment_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_assignment_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','assignment','tagged_id_assignment_id'); + +CREATE TRIGGER tr_log_tagged_id_motion_id_gm_tag_tagged_ids_t AFTER INSERT OR UPDATE OF tagged_id_motion_id OR DELETE ON gm_tag_tagged_ids_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('tag','tag_id','motion','tagged_id_motion_id'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON gm_tag_tagged_ids_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_tag_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON tag_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_import_preview AFTER INSERT OR UPDATE OR DELETE ON import_preview_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('import_preview'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON import_preview_t +CREATE TRIGGER tr_log_theme AFTER INSERT OR UPDATE OR DELETE ON theme_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('theme'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON theme_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_history_position AFTER INSERT OR UPDATE OR DELETE ON history_position_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_position'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_position_t +CREATE TRIGGER tr_log_topic AFTER INSERT OR UPDATE OR DELETE ON topic_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('topic'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON topic_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_history_position_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON history_position_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_topic_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON topic_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); -CREATE TRIGGER tr_log_history_entry AFTER INSERT OR UPDATE OR DELETE ON history_entry_t -FOR EACH ROW EXECUTE FUNCTION log_modified_models('history_entry'); -CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON history_entry_t +CREATE TRIGGER tr_log_user AFTER INSERT OR UPDATE OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('user'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON user_t DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); +CREATE TRIGGER tr_log_user_t_gender_id AFTER INSERT OR UPDATE OF gender_id OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('gender', 'gender_id'); +CREATE TRIGGER tr_log_user_t_home_committee_id AFTER INSERT OR UPDATE OF home_committee_id OR DELETE ON user_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('committee', 'home_committee_id'); -CREATE TRIGGER tr_log_user_model_id_user_id AFTER INSERT OR UPDATE OF model_id_user_id OR DELETE ON history_entry_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user','model_id_user_id'); - -CREATE TRIGGER tr_log_motion_model_id_motion_id AFTER INSERT OR UPDATE OF model_id_motion_id OR DELETE ON history_entry_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('motion','model_id_motion_id'); +CREATE TRIGGER tr_log_vote AFTER INSERT OR UPDATE OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_models('vote'); +CREATE CONSTRAINT TRIGGER notify_transaction_end AFTER INSERT OR UPDATE OR DELETE ON vote_t +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION notify_transaction_end(); -CREATE TRIGGER tr_log_assignment_model_id_assignment_id AFTER INSERT OR UPDATE OF model_id_assignment_id OR DELETE ON history_entry_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('assignment','model_id_assignment_id'); -CREATE TRIGGER tr_log_history_entry_t_position_id AFTER INSERT OR UPDATE OF position_id OR DELETE ON history_entry_t -FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('history_position', 'position_id'); -CREATE TRIGGER tr_log_history_entry_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON history_entry_t +CREATE TRIGGER tr_log_vote_t_option_id AFTER INSERT OR UPDATE OF option_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('option', 'option_id'); +CREATE TRIGGER tr_log_vote_t_user_id AFTER INSERT OR UPDATE OF user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'user_id'); +CREATE TRIGGER tr_log_vote_t_delegated_user_id AFTER INSERT OR UPDATE OF delegated_user_id OR DELETE ON vote_t +FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('user', 'delegated_user_id'); +CREATE TRIGGER tr_log_vote_t_meeting_id AFTER INSERT OR UPDATE OF meeting_id OR DELETE ON vote_t FOR EACH ROW EXECUTE FUNCTION log_modified_related_models('meeting', 'meeting_id'); @@ -3236,53 +3243,35 @@ Model.Field -> Model.Field */ /* -SQL nr:1rR => organization/gender_ids:-> gender/organization_id -SQL nr:1rR => organization/committee_ids:-> committee/organization_id -SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id -SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id -SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id -SQL nr:1rR => organization/organization_tag_ids:-> organization_tag/organization_id -FIELD 1rR:1t => organization/theme_id:-> theme/theme_for_organization_id -SQL nr:1rR => organization/theme_ids:-> theme/organization_id -SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id -SQL nt:1r => organization/published_mediafile_ids:-> mediafile/published_to_meetings_in_organization_id -SQL nr:1rR => organization/user_ids:-> user/organization_id - -FIELD 1r:nr => user/gender_id:-> gender/user_ids -SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids -SQL nts:nts => user/committee_ids:-> committee/user_ids -SQL nt:nt => user/committee_management_ids:-> committee/manager_ids -SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id -SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids -SQL nr:1Gr => user/option_ids:-> option/content_object_id -SQL nr:1r => user/vote_ids:-> vote/user_id -SQL nr:1r => user/delegated_vote_ids:-> vote/delegated_user_id -SQL nr:1r => user/poll_candidate_ids:-> poll_candidate/user_id -FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids -SQL nt:1r => user/history_position_ids:-> history_position/user_id -SQL nt:1Gr => user/history_entry_ids:-> history_entry/model_id -SQL nts:nts => user/meeting_ids:-> meeting/user_ids +FIELD 1GrR:1t,1t,1t,1tR => agenda_item/content_object_id:-> motion/agenda_item_id,motion_block/agenda_item_id,assignment/agenda_item_id,topic/agenda_item_id +FIELD 1r:nt => agenda_item/parent_id:-> agenda_item/child_ids +SQL nt:1r => agenda_item/child_ids:-> agenda_item/parent_id +SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids +SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids -FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids -FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids -SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id -SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id -SQL nt:1r => meeting_user/motion_supporter_ids:-> motion_supporter/meeting_user_id -SQL nt:1r => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id -SQL nt:1r => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id -SQL nt:1r => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id -SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id -FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids -SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id -SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id -SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids -SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids +SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id +SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id +SQL 1t:1GrR => assignment/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids +SQL nt:nGt => assignment/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids +SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => assignment/meeting_id:-> meeting/assignment_ids +SQL nt:1Gr => assignment/history_entry_ids:-> history_entry/model_id -SQL nr:1r => gender/user_ids:-> user/gender_id +FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids +FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids +FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids -SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids +SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id +SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids +SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids +FIELD 1rR:nt => chat_group/meeting_id:-> meeting/chat_group_ids -SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id +FIELD 1r:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids +FIELD 1rR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids +FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids SQL nt:1rR => committee/meeting_ids:-> meeting/committee_id FIELD 1r:1t => committee/default_meeting_id:-> meeting/default_meeting_for_committee_id @@ -3297,6 +3286,44 @@ SQL nt:nt => committee/forward_to_committee_ids:-> committee/receive_forwardings SQL nt:nt => committee/receive_forwardings_from_committee_ids:-> committee/forward_to_committee_ids SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids +SQL nr:1r => gender/user_ids:-> user/gender_id + +SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids +SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id +SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id +SQL 1t:1r => group/anonymous_group_for_meeting_id:-> meeting/anonymous_group_id +SQL nt:nt => group/meeting_mediafile_access_group_ids:-> meeting_mediafile/access_group_ids +SQL nt:nt => group/meeting_mediafile_inherited_access_group_ids:-> meeting_mediafile/inherited_access_group_ids +SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids +SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_group_ids +SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids +SQL nt:nt => group/write_chat_group_ids:-> chat_group/write_group_ids +SQL nt:nt => group/poll_ids:-> poll/entitled_group_ids +FIELD 1r:nt => group/used_as_motion_poll_default_id:-> meeting/motion_poll_default_group_ids +FIELD 1r:nt => group/used_as_assignment_poll_default_id:-> meeting/assignment_poll_default_group_ids +FIELD 1r:nt => group/used_as_topic_poll_default_id:-> meeting/topic_poll_default_group_ids +FIELD 1r:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids +FIELD 1rR:nt => group/meeting_id:-> meeting/group_ids + +FIELD 1Gr:nt,nt,nt => history_entry/model_id:-> user/history_entry_ids,motion/history_entry_ids,assignment/history_entry_ids +FIELD 1rR:nt => history_entry/position_id:-> history_position/entry_ids +FIELD 1r:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids + +FIELD 1r:nt => history_position/user_id:-> user/history_position_ids +SQL nt:1rR => history_position/entry_ids:-> history_entry/position_id + +FIELD 1GrR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,meeting_mediafile/list_of_speakers_id +SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id +SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id +SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids + +FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids +FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids +SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id +FIELD 1GrR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids +SQL nt:1rR => mediafile/meeting_mediafile_ids:-> meeting_mediafile/mediafile_id + FIELD 1r:nt => meeting/is_active_in_organization_id:-> organization/active_meeting_ids FIELD 1r:nt => meeting/is_archived_in_organization_id:-> organization/archived_meeting_ids FIELD 1r:nt => meeting/template_for_organization_id:-> organization/template_meeting_ids @@ -3385,71 +3412,48 @@ SQL ntR:1r => meeting/default_projector_assignment_poll_ids:-> projector/used_as SQL ntR:1r => meeting/default_projector_motion_poll_ids:-> projector/used_as_default_projector_for_motion_poll_in_meeting_id SQL ntR:1r => meeting/default_projector_poll_ids:-> projector/used_as_default_projector_for_poll_in_meeting_id FIELD 1rR:1t => meeting/default_group_id:-> group/default_group_for_meeting_id -FIELD 1r:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id -FIELD 1r:1t => meeting/anonymous_group_id:-> group/anonymous_group_for_meeting_id -SQL nt:1r => meeting/relevant_history_entry_ids:-> history_entry/meeting_id - -SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids -SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id -FIELD 1rR:nt => structure_level/meeting_id:-> meeting/structure_level_ids - -SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids -SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id -SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id -SQL 1t:1r => group/anonymous_group_for_meeting_id:-> meeting/anonymous_group_id -SQL nt:nt => group/meeting_mediafile_access_group_ids:-> meeting_mediafile/access_group_ids -SQL nt:nt => group/meeting_mediafile_inherited_access_group_ids:-> meeting_mediafile/inherited_access_group_ids -SQL nt:nt => group/read_comment_section_ids:-> motion_comment_section/read_group_ids -SQL nt:nt => group/write_comment_section_ids:-> motion_comment_section/write_group_ids -SQL nt:nt => group/read_chat_group_ids:-> chat_group/read_group_ids -SQL nt:nt => group/write_chat_group_ids:-> chat_group/write_group_ids -SQL nt:nt => group/poll_ids:-> poll/entitled_group_ids -FIELD 1r:nt => group/used_as_motion_poll_default_id:-> meeting/motion_poll_default_group_ids -FIELD 1r:nt => group/used_as_assignment_poll_default_id:-> meeting/assignment_poll_default_group_ids -FIELD 1r:nt => group/used_as_topic_poll_default_id:-> meeting/topic_poll_default_group_ids -FIELD 1r:nt => group/used_as_poll_default_id:-> meeting/poll_default_group_ids -FIELD 1rR:nt => group/meeting_id:-> meeting/group_ids - -FIELD 1rR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids -FIELD 1Gr:nt => personal_note/content_object_id:-> motion/personal_note_ids -FIELD 1rR:nt => personal_note/meeting_id:-> meeting/personal_note_ids - -SQL nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids -FIELD 1rR:nt => tag/meeting_id:-> meeting/tag_ids - -FIELD 1GrR:1t,1t,1t,1tR => agenda_item/content_object_id:-> motion/agenda_item_id,motion_block/agenda_item_id,assignment/agenda_item_id,topic/agenda_item_id -FIELD 1r:nt => agenda_item/parent_id:-> agenda_item/child_ids -SQL nt:1r => agenda_item/child_ids:-> agenda_item/parent_id -SQL nt:nGt => agenda_item/tag_ids:-> tag/tagged_ids -SQL nt:1GrR => agenda_item/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => agenda_item/meeting_id:-> meeting/agenda_item_ids - -FIELD 1GrR:1tR,1tR,1tR,1tR,1t => list_of_speakers/content_object_id:-> motion/list_of_speakers_id,motion_block/list_of_speakers_id,assignment/list_of_speakers_id,topic/list_of_speakers_id,meeting_mediafile/list_of_speakers_id -SQL nt:1rR => list_of_speakers/speaker_ids:-> speaker/list_of_speakers_id -SQL nt:1rR => list_of_speakers/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/list_of_speakers_id -SQL nt:1GrR => list_of_speakers/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => list_of_speakers/meeting_id:-> meeting/list_of_speakers_ids - -FIELD 1rR:nt => structure_level_list_of_speakers/structure_level_id:-> structure_level/structure_level_list_of_speakers_ids -FIELD 1rR:nt => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/structure_level_list_of_speakers_ids -SQL nt:1r => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id -FIELD 1rR:nt => structure_level_list_of_speakers/meeting_id:-> meeting/structure_level_list_of_speakers_ids - -FIELD 1rR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids -SQL nt:1r => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id +FIELD 1r:1t => meeting/admin_group_id:-> group/admin_group_for_meeting_id +FIELD 1r:1t => meeting/anonymous_group_id:-> group/anonymous_group_for_meeting_id +SQL nt:1r => meeting/relevant_history_entry_ids:-> history_entry/meeting_id -FIELD 1rR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids -FIELD 1r:nt => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/speaker_ids -FIELD 1r:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids -FIELD 1r:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids -FIELD 1rR:nt => speaker/meeting_id:-> meeting/speaker_ids +FIELD 1rR:nt => meeting_mediafile/mediafile_id:-> mediafile/meeting_mediafile_ids +FIELD 1rR:nt => meeting_mediafile/meeting_id:-> meeting/meeting_mediafile_ids +SQL nt:nt => meeting_mediafile/inherited_access_group_ids:-> group/meeting_mediafile_inherited_access_group_ids +SQL nt:nt => meeting_mediafile/access_group_ids:-> group/meeting_mediafile_access_group_ids +SQL 1t:1GrR => meeting_mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => meeting_mediafile/projection_ids:-> projection/content_object_id +SQL nGt:nt,nt,nt => meeting_mediafile/attachment_ids:-> motion/attachment_meeting_mediafile_ids,topic/attachment_meeting_mediafile_ids,assignment/attachment_meeting_mediafile_ids +SQL 1t:1r => meeting_mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id +SQL 1t:1r => meeting_mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id +SQL 1t:1r => meeting_mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id +SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id +SQL 1t:1r => meeting_mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id +SQL 1t:1r => meeting_mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id +SQL 1t:1r => meeting_mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id +SQL 1t:1r => meeting_mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id +SQL 1t:1r => meeting_mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id +SQL 1t:1r => meeting_mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id +SQL 1t:1r => meeting_mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id +SQL 1t:1r => meeting_mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id -SQL nt:nGt => topic/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids -SQL 1tR:1GrR => topic/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GrR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GrR => topic/poll_ids:-> poll/content_object_id -SQL nt:1GrR => topic/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => topic/meeting_id:-> meeting/topic_ids +FIELD 1rR:nt => meeting_user/user_id:-> user/meeting_user_ids +FIELD 1rR:nt => meeting_user/meeting_id:-> meeting/meeting_user_ids +SQL nt:1rR => meeting_user/personal_note_ids:-> personal_note/meeting_user_id +SQL nt:1r => meeting_user/speaker_ids:-> speaker/meeting_user_id +SQL nt:1r => meeting_user/motion_supporter_ids:-> motion_supporter/meeting_user_id +SQL nt:1r => meeting_user/motion_editor_ids:-> motion_editor/meeting_user_id +SQL nt:1r => meeting_user/motion_working_group_speaker_ids:-> motion_working_group_speaker/meeting_user_id +SQL nt:1r => meeting_user/motion_submitter_ids:-> motion_submitter/meeting_user_id +SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeting_user_id +FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids +SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id +SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id +SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids +SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids FIELD 1r:nt => motion/lead_motion_id:-> motion/amendment_ids SQL nt:1r => motion/amendment_ids:-> motion/lead_motion_id @@ -3486,21 +3490,19 @@ SQL nt:1Gr => motion/personal_note_ids:-> personal_note/content_object_id FIELD 1rR:nt => motion/meeting_id:-> meeting/motion_ids SQL nt:1Gr => motion/history_entry_ids:-> history_entry/model_id -FIELD 1r:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids -FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids -FIELD 1rR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids - -FIELD 1r:nt => motion_supporter/meeting_user_id:-> meeting_user/motion_supporter_ids -FIELD 1rR:nt => motion_supporter/motion_id:-> motion/supporter_ids -FIELD 1rR:nt => motion_supporter/meeting_id:-> meeting/motion_supporter_ids +SQL nt:1r => motion_block/motion_ids:-> motion/block_id +SQL 1t:1GrR => motion_block/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => motion_block/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => motion_block/meeting_id:-> meeting/motion_block_ids -FIELD 1r:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids -FIELD 1rR:nt => motion_editor/motion_id:-> motion/editor_ids -FIELD 1rR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids +FIELD 1r:nt => motion_category/parent_id:-> motion_category/child_ids +SQL nt:1r => motion_category/child_ids:-> motion_category/parent_id +SQL nt:1r => motion_category/motion_ids:-> motion/category_id +FIELD 1rR:nt => motion_category/meeting_id:-> meeting/motion_category_ids -FIELD 1r:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids -FIELD 1rR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids -FIELD 1rR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids +FIELD 1rR:nt => motion_change_recommendation/motion_id:-> motion/change_recommendation_ids +FIELD 1rR:nt => motion_change_recommendation/meeting_id:-> meeting/motion_change_recommendation_ids FIELD 1rR:nt => motion_comment/motion_id:-> motion/comment_ids FIELD 1rR:nt => motion_comment/section_id:-> motion_comment_section/comment_ids @@ -3511,19 +3513,9 @@ SQL nt:nt => motion_comment_section/read_group_ids:-> group/read_comment_section SQL nt:nt => motion_comment_section/write_group_ids:-> group/write_comment_section_ids FIELD 1rR:nt => motion_comment_section/meeting_id:-> meeting/motion_comment_section_ids -FIELD 1r:nt => motion_category/parent_id:-> motion_category/child_ids -SQL nt:1r => motion_category/child_ids:-> motion_category/parent_id -SQL nt:1r => motion_category/motion_ids:-> motion/category_id -FIELD 1rR:nt => motion_category/meeting_id:-> meeting/motion_category_ids - -SQL nt:1r => motion_block/motion_ids:-> motion/block_id -SQL 1t:1GrR => motion_block/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GrR => motion_block/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GrR => motion_block/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => motion_block/meeting_id:-> meeting/motion_block_ids - -FIELD 1rR:nt => motion_change_recommendation/motion_id:-> motion/change_recommendation_ids -FIELD 1rR:nt => motion_change_recommendation/meeting_id:-> meeting/motion_change_recommendation_ids +FIELD 1r:nt => motion_editor/meeting_user_id:-> meeting_user/motion_editor_ids +FIELD 1rR:nt => motion_editor/motion_id:-> motion/editor_ids +FIELD 1rR:nt => motion_editor/meeting_id:-> meeting/motion_editor_ids FIELD 1r:nt => motion_state/submitter_withdraw_state_id:-> motion_state/submitter_withdraw_back_ids SQL nt:1r => motion_state/submitter_withdraw_back_ids:-> motion_state/submitter_withdraw_state_id @@ -3535,19 +3527,23 @@ FIELD 1rR:nt => motion_state/workflow_id:-> motion_workflow/state_ids SQL 1t:1rR => motion_state/first_state_of_workflow_id:-> motion_workflow/first_state_id FIELD 1rR:nt => motion_state/meeting_id:-> meeting/motion_state_ids +FIELD 1r:nt => motion_submitter/meeting_user_id:-> meeting_user/motion_submitter_ids +FIELD 1rR:nt => motion_submitter/motion_id:-> motion/submitter_ids +FIELD 1rR:nt => motion_submitter/meeting_id:-> meeting/motion_submitter_ids + +FIELD 1r:nt => motion_supporter/meeting_user_id:-> meeting_user/motion_supporter_ids +FIELD 1rR:nt => motion_supporter/motion_id:-> motion/supporter_ids +FIELD 1rR:nt => motion_supporter/meeting_id:-> meeting/motion_supporter_ids + SQL nt:1rR => motion_workflow/state_ids:-> motion_state/workflow_id FIELD 1rR:1t => motion_workflow/first_state_id:-> motion_state/first_state_of_workflow_id SQL 1t:1rR => motion_workflow/default_workflow_meeting_id:-> meeting/motions_default_workflow_id SQL 1t:1rR => motion_workflow/default_amendment_workflow_meeting_id:-> meeting/motions_default_amendment_workflow_id FIELD 1rR:nt => motion_workflow/meeting_id:-> meeting/motion_workflow_ids -FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids -SQL nr:1r => poll/option_ids:-> option/poll_id -FIELD 1r:1r => poll/global_option_id:-> option/used_as_global_option_in_poll_id -SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids -SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids -SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id -FIELD 1rR:nr => poll/meeting_id:-> meeting/poll_ids +FIELD 1r:nt => motion_working_group_speaker/meeting_user_id:-> meeting_user/motion_working_group_speaker_ids +FIELD 1rR:nt => motion_working_group_speaker/motion_id:-> motion/working_group_speaker_ids +FIELD 1rR:nt => motion_working_group_speaker/meeting_id:-> meeting/motion_working_group_speaker_ids FIELD 1r:nr => option/poll_id:-> poll/option_ids FIELD 1r:1r => option/used_as_global_option_in_poll_id:-> poll/global_option_id @@ -3555,62 +3551,48 @@ SQL nr:1rR => option/vote_ids:-> vote/option_id FIELD 1Gr:nr,nr,1tR => option/content_object_id:-> motion/option_ids,user/option_ids,poll_candidate_list/option_id FIELD 1rR:nr => option/meeting_id:-> meeting/option_ids -FIELD 1rR:nr => vote/option_id:-> option/vote_ids -FIELD 1r:nr => vote/user_id:-> user/vote_ids -FIELD 1r:nr => vote/delegated_user_id:-> user/delegated_vote_ids -FIELD 1rR:nr => vote/meeting_id:-> meeting/vote_ids +SQL nr:1rR => organization/gender_ids:-> gender/organization_id +SQL nr:1rR => organization/committee_ids:-> committee/organization_id +SQL nt:1r => organization/active_meeting_ids:-> meeting/is_active_in_organization_id +SQL nt:1r => organization/archived_meeting_ids:-> meeting/is_archived_in_organization_id +SQL nt:1r => organization/template_meeting_ids:-> meeting/template_for_organization_id +SQL nr:1rR => organization/organization_tag_ids:-> organization_tag/organization_id +FIELD 1rR:1t => organization/theme_id:-> theme/theme_for_organization_id +SQL nr:1rR => organization/theme_ids:-> theme/organization_id +SQL nt:1GrR => organization/mediafile_ids:-> mediafile/owner_id +SQL nt:1r => organization/published_mediafile_ids:-> mediafile/published_to_meetings_in_organization_id +SQL nr:1rR => organization/user_ids:-> user/organization_id -SQL nt:1rR => assignment/candidate_ids:-> assignment_candidate/assignment_id -SQL nt:1GrR => assignment/poll_ids:-> poll/content_object_id -SQL 1t:1GrR => assignment/agenda_item_id:-> agenda_item/content_object_id -SQL 1tR:1GrR => assignment/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:nGt => assignment/tag_ids:-> tag/tagged_ids -SQL nt:nGt => assignment/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids -SQL nt:1GrR => assignment/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => assignment/meeting_id:-> meeting/assignment_ids -SQL nt:1Gr => assignment/history_entry_ids:-> history_entry/model_id +SQL nGt:nt,nt => organization_tag/tagged_ids:-> committee/organization_tag_ids,meeting/organization_tag_ids -FIELD 1rR:nt => assignment_candidate/assignment_id:-> assignment/candidate_ids -FIELD 1r:nt => assignment_candidate/meeting_user_id:-> meeting_user/assignment_candidate_ids -FIELD 1rR:nt => assignment_candidate/meeting_id:-> meeting/assignment_candidate_ids +FIELD 1rR:nt => personal_note/meeting_user_id:-> meeting_user/personal_note_ids +FIELD 1Gr:nt => personal_note/content_object_id:-> motion/personal_note_ids +FIELD 1rR:nt => personal_note/meeting_id:-> meeting/personal_note_ids -SQL nr:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id -FIELD 1rR:nr => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids -SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id +FIELD 1rR:nt => point_of_order_category/meeting_id:-> meeting/point_of_order_category_ids +SQL nt:1r => point_of_order_category/speaker_ids:-> speaker/point_of_order_category_id + +FIELD 1GrR:nt,nt,nt => poll/content_object_id:-> motion/poll_ids,assignment/poll_ids,topic/poll_ids +SQL nr:1r => poll/option_ids:-> option/poll_id +FIELD 1r:1r => poll/global_option_id:-> option/used_as_global_option_in_poll_id +SQL nt:nt => poll/voted_ids:-> user/poll_voted_ids +SQL nt:nt => poll/entitled_group_ids:-> group/poll_ids +SQL nt:1GrR => poll/projection_ids:-> projection/content_object_id +FIELD 1rR:nr => poll/meeting_id:-> meeting/poll_ids FIELD 1rR:nr => poll_candidate/poll_candidate_list_id:-> poll_candidate_list/poll_candidate_ids FIELD 1r:nr => poll_candidate/user_id:-> user/poll_candidate_ids FIELD 1rR:nr => poll_candidate/meeting_id:-> meeting/poll_candidate_ids -FIELD 1r:nt => mediafile/published_to_meetings_in_organization_id:-> organization/published_mediafile_ids -FIELD 1r:nt => mediafile/parent_id:-> mediafile/child_ids -SQL nt:1r => mediafile/child_ids:-> mediafile/parent_id -FIELD 1GrR:nt,nt => mediafile/owner_id:-> meeting/mediafile_ids,organization/mediafile_ids -SQL nt:1rR => mediafile/meeting_mediafile_ids:-> meeting_mediafile/mediafile_id +SQL nr:1rR => poll_candidate_list/poll_candidate_ids:-> poll_candidate/poll_candidate_list_id +FIELD 1rR:nr => poll_candidate_list/meeting_id:-> meeting/poll_candidate_list_ids +SQL 1tR:1Gr => poll_candidate_list/option_id:-> option/content_object_id -FIELD 1rR:nt => meeting_mediafile/mediafile_id:-> mediafile/meeting_mediafile_ids -FIELD 1rR:nt => meeting_mediafile/meeting_id:-> meeting/meeting_mediafile_ids -SQL nt:nt => meeting_mediafile/inherited_access_group_ids:-> group/meeting_mediafile_inherited_access_group_ids -SQL nt:nt => meeting_mediafile/access_group_ids:-> group/meeting_mediafile_access_group_ids -SQL 1t:1GrR => meeting_mediafile/list_of_speakers_id:-> list_of_speakers/content_object_id -SQL nt:1GrR => meeting_mediafile/projection_ids:-> projection/content_object_id -SQL nGt:nt,nt,nt => meeting_mediafile/attachment_ids:-> motion/attachment_meeting_mediafile_ids,topic/attachment_meeting_mediafile_ids,assignment/attachment_meeting_mediafile_ids -SQL 1t:1r => meeting_mediafile/used_as_logo_projector_main_in_meeting_id:-> meeting/logo_projector_main_id -SQL 1t:1r => meeting_mediafile/used_as_logo_projector_header_in_meeting_id:-> meeting/logo_projector_header_id -SQL 1t:1r => meeting_mediafile/used_as_logo_web_header_in_meeting_id:-> meeting/logo_web_header_id -SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_l_in_meeting_id:-> meeting/logo_pdf_header_l_id -SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_header_r_in_meeting_id:-> meeting/logo_pdf_header_r_id -SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_l_in_meeting_id:-> meeting/logo_pdf_footer_l_id -SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_footer_r_in_meeting_id:-> meeting/logo_pdf_footer_r_id -SQL 1t:1r => meeting_mediafile/used_as_logo_pdf_ballot_paper_in_meeting_id:-> meeting/logo_pdf_ballot_paper_id -SQL 1t:1r => meeting_mediafile/used_as_font_regular_in_meeting_id:-> meeting/font_regular_id -SQL 1t:1r => meeting_mediafile/used_as_font_italic_in_meeting_id:-> meeting/font_italic_id -SQL 1t:1r => meeting_mediafile/used_as_font_bold_in_meeting_id:-> meeting/font_bold_id -SQL 1t:1r => meeting_mediafile/used_as_font_bold_italic_in_meeting_id:-> meeting/font_bold_italic_id -SQL 1t:1r => meeting_mediafile/used_as_font_monospace_in_meeting_id:-> meeting/font_monospace_id -SQL 1t:1r => meeting_mediafile/used_as_font_chyron_speaker_name_in_meeting_id:-> meeting/font_chyron_speaker_name_id -SQL 1t:1r => meeting_mediafile/used_as_font_projector_h1_in_meeting_id:-> meeting/font_projector_h1_id -SQL 1t:1r => meeting_mediafile/used_as_font_projector_h2_in_meeting_id:-> meeting/font_projector_h2_id +FIELD 1r:nt => projection/current_projector_id:-> projector/current_projection_ids +FIELD 1r:nt => projection/preview_projector_id:-> projector/preview_projection_ids +FIELD 1r:nt => projection/history_projector_id:-> projector/history_projection_ids +FIELD 1GrR:nt,nt,nt,nt,nt,nt,nt,nt,nt,nt,nt => projection/content_object_id:-> meeting/projection_ids,motion/projection_ids,meeting_mediafile/projection_ids,list_of_speakers/projection_ids,motion_block/projection_ids,assignment/projection_ids,agenda_item/projection_ids,topic/projection_ids,poll/projection_ids,projector_message/projection_ids,projector_countdown/projection_ids +FIELD 1rR:nt => projection/meeting_id:-> meeting/all_projection_ids SQL nt:1r => projector/current_projection_ids:-> projection/current_projector_id SQL nt:1r => projector/preview_projection_ids:-> projection/preview_projector_id @@ -3632,35 +3614,60 @@ FIELD 1r:ntR => projector/used_as_default_projector_for_motion_poll_in_meeting_i FIELD 1r:ntR => projector/used_as_default_projector_for_poll_in_meeting_id:-> meeting/default_projector_poll_ids FIELD 1rR:nt => projector/meeting_id:-> meeting/projector_ids -FIELD 1r:nt => projection/current_projector_id:-> projector/current_projection_ids -FIELD 1r:nt => projection/preview_projector_id:-> projector/preview_projection_ids -FIELD 1r:nt => projection/history_projector_id:-> projector/history_projection_ids -FIELD 1GrR:nt,nt,nt,nt,nt,nt,nt,nt,nt,nt,nt => projection/content_object_id:-> meeting/projection_ids,motion/projection_ids,meeting_mediafile/projection_ids,list_of_speakers/projection_ids,motion_block/projection_ids,assignment/projection_ids,agenda_item/projection_ids,topic/projection_ids,poll/projection_ids,projector_message/projection_ids,projector_countdown/projection_ids -FIELD 1rR:nt => projection/meeting_id:-> meeting/all_projection_ids - -SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id -FIELD 1rR:nt => projector_message/meeting_id:-> meeting/projector_message_ids - SQL nt:1GrR => projector_countdown/projection_ids:-> projection/content_object_id SQL 1t:1r => projector_countdown/used_as_list_of_speakers_countdown_meeting_id:-> meeting/list_of_speakers_countdown_id SQL 1t:1r => projector_countdown/used_as_poll_countdown_meeting_id:-> meeting/poll_countdown_id FIELD 1rR:nt => projector_countdown/meeting_id:-> meeting/projector_countdown_ids -SQL nt:1rR => chat_group/chat_message_ids:-> chat_message/chat_group_id -SQL nt:nt => chat_group/read_group_ids:-> group/read_chat_group_ids -SQL nt:nt => chat_group/write_group_ids:-> group/write_chat_group_ids -FIELD 1rR:nt => chat_group/meeting_id:-> meeting/chat_group_ids +SQL nt:1GrR => projector_message/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => projector_message/meeting_id:-> meeting/projector_message_ids -FIELD 1r:nt => chat_message/meeting_user_id:-> meeting_user/chat_message_ids -FIELD 1rR:nt => chat_message/chat_group_id:-> chat_group/chat_message_ids -FIELD 1rR:nt => chat_message/meeting_id:-> meeting/chat_message_ids +FIELD 1rR:nt => speaker/list_of_speakers_id:-> list_of_speakers/speaker_ids +FIELD 1r:nt => speaker/structure_level_list_of_speakers_id:-> structure_level_list_of_speakers/speaker_ids +FIELD 1r:nt => speaker/meeting_user_id:-> meeting_user/speaker_ids +FIELD 1r:nt => speaker/point_of_order_category_id:-> point_of_order_category/speaker_ids +FIELD 1rR:nt => speaker/meeting_id:-> meeting/speaker_ids -FIELD 1r:nt => history_position/user_id:-> user/history_position_ids -SQL nt:1rR => history_position/entry_ids:-> history_entry/position_id +SQL nt:nt => structure_level/meeting_user_ids:-> meeting_user/structure_level_ids +SQL nt:1rR => structure_level/structure_level_list_of_speakers_ids:-> structure_level_list_of_speakers/structure_level_id +FIELD 1rR:nt => structure_level/meeting_id:-> meeting/structure_level_ids -FIELD 1Gr:nt,nt,nt => history_entry/model_id:-> user/history_entry_ids,motion/history_entry_ids,assignment/history_entry_ids -FIELD 1rR:nt => history_entry/position_id:-> history_position/entry_ids -FIELD 1r:nt => history_entry/meeting_id:-> meeting/relevant_history_entry_ids +FIELD 1rR:nt => structure_level_list_of_speakers/structure_level_id:-> structure_level/structure_level_list_of_speakers_ids +FIELD 1rR:nt => structure_level_list_of_speakers/list_of_speakers_id:-> list_of_speakers/structure_level_list_of_speakers_ids +SQL nt:1r => structure_level_list_of_speakers/speaker_ids:-> speaker/structure_level_list_of_speakers_id +FIELD 1rR:nt => structure_level_list_of_speakers/meeting_id:-> meeting/structure_level_list_of_speakers_ids + +SQL nGt:nt,nt,nt => tag/tagged_ids:-> agenda_item/tag_ids,assignment/tag_ids,motion/tag_ids +FIELD 1rR:nt => tag/meeting_id:-> meeting/tag_ids + +SQL 1t:1rR => theme/theme_for_organization_id:-> organization/theme_id + +SQL nt:nGt => topic/attachment_meeting_mediafile_ids:-> meeting_mediafile/attachment_ids +SQL 1tR:1GrR => topic/agenda_item_id:-> agenda_item/content_object_id +SQL 1tR:1GrR => topic/list_of_speakers_id:-> list_of_speakers/content_object_id +SQL nt:1GrR => topic/poll_ids:-> poll/content_object_id +SQL nt:1GrR => topic/projection_ids:-> projection/content_object_id +FIELD 1rR:nt => topic/meeting_id:-> meeting/topic_ids + +FIELD 1r:nr => user/gender_id:-> gender/user_ids +SQL nt:nt => user/is_present_in_meeting_ids:-> meeting/present_user_ids +SQL nts:nts => user/committee_ids:-> committee/user_ids +SQL nt:nt => user/committee_management_ids:-> committee/manager_ids +SQL nt:1rR => user/meeting_user_ids:-> meeting_user/user_id +SQL nt:nt => user/poll_voted_ids:-> poll/voted_ids +SQL nr:1Gr => user/option_ids:-> option/content_object_id +SQL nr:1r => user/vote_ids:-> vote/user_id +SQL nr:1r => user/delegated_vote_ids:-> vote/delegated_user_id +SQL nr:1r => user/poll_candidate_ids:-> poll_candidate/user_id +FIELD 1r:nt => user/home_committee_id:-> committee/native_user_ids +SQL nt:1r => user/history_position_ids:-> history_position/user_id +SQL nt:1Gr => user/history_entry_ids:-> history_entry/model_id +SQL nts:nts => user/meeting_ids:-> meeting/user_ids + +FIELD 1rR:nr => vote/option_id:-> option/vote_ids +FIELD 1r:nr => vote/user_id:-> user/vote_ids +FIELD 1r:nr => vote/delegated_user_id:-> user/delegated_vote_ids +FIELD 1rR:nr => vote/meeting_id:-> meeting/vote_ids */ /* @@ -3669,4 +3676,4 @@ There are 2 errors/warnings projection/content: type:JSON is marked as a calculated field and not generated in schema */ -/* Missing attribute handling for constant, on_delete, equal_fields, deferred */ \ No newline at end of file +/* Missing attribute handling for constant, equal_fields, on_delete, deferred */ \ No newline at end of file diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 75cb6c69..6ff69404 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -68,7 +68,8 @@ class SubstDict(TypedDict, total=False): class GenerateCodeBlocks: """Main work is done here by recursing the models and their fields and determine the method to use""" - models = MODELS + if not InternalHelper.MODELS: + InternalHelper.read_models_yml(SOURCE.as_posix()) intermediate_tables: dict[str, str] = ( {} ) # Key=Name, data: collected content of table @@ -131,10 +132,8 @@ def generate_the_code( missing_handled_attributes = [] im_table_code = "" errors: list[str] = [] - if not cls.models: - cls.models = MODELS - for table_name, fields in cls.models.items(): + for table_name, fields in InternalHelper.MODELS.items(): if table_name in ["_migration_index", "_meta"]: continue @@ -813,7 +812,7 @@ class Helper: operation_var := LOWER(TG_OP); fqid_var := escaped_table_name || '/' || NEW.id; IF (TG_OP = 'DELETE') THEN - fqid_var = escaped_table_name || '/' || OLD.id; + fqid_var := escaped_table_name || '/' || OLD.id; END IF; INSERT INTO os_notify_log_t (operation, fqid, xact_id, timestamp) @@ -834,7 +833,7 @@ class Helper: value_1 integer; value_2 integer; BEGIN - base_column_name = TG_ARGV[0]; + base_column_name := TG_ARGV[0]; value_1 := hstore(NEW) -> (base_column_name || '_1'); value_2 := hstore(NEW) -> (base_column_name || '_2'); @@ -852,7 +851,7 @@ class Helper: payload TEXT; body_content_text TEXT; BEGIN - -- Running the trigger for the first time in a transaction creates the table and after commiting the transaction the table is dropped. + -- Running the trigger for the first time in a transaction creates the table and after committing the transaction the table is dropped. -- Every next run of the trigger in this transaction raises a notice that the table exists. Setting the log_min_messages to notice increases the noise because of such messages. CREATE LOCAL TEMPORARY TABLE IF NOT EXISTS tbl_notify_counter_tx_once ( @@ -914,6 +913,18 @@ class Helper: timestamp timestamptz, CONSTRAINT unique_fqid_xact_id_operation UNIQUE (operation,fqid,xact_id) ); + + CREATE TABLE version ( + migration_index INTEGER PRIMARY KEY, + migration_state TEXT, + replace_tables JSONB + ); + + CREATE OR REPLACE FUNCTION prevent_writes() RETURNS trigger AS $read_only_trigger$ + BEGIN + RAISE EXCEPTION 'Table % is currently read-only.', TG_TABLE_NAME; + END; + $read_only_trigger$ LANGUAGE plpgsql; """ ) @@ -923,7 +934,7 @@ class Helper: }.items(): FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( f""" - CREATE FUNCTION check_not_null_for_{type_}() RETURNS trigger as $not_null_trigger$ + CREATE FUNCTION check_not_null_for_{type_}() RETURNS trigger AS $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name: relation to check, usually a view -- column_name: field to check, usually a field in a view @@ -983,7 +994,6 @@ class Helper: dedent( """ CREATE TABLE ${table_name} ( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, ${own_table_name_with_ref_column} integer NOT NULL REFERENCES ${own_table_name}(${own_table_ref_column}) ON DELETE CASCADE INITIALLY DEFERRED, ${own_table_column} varchar(100) NOT NULL, ${foreign_table_ref_lines} @@ -1402,7 +1412,7 @@ def is_fk_initially_deferred(own_table: str, foreign_table: str) -> bool: """ def _first_to_second(t1: str, t2: str) -> bool: - for field in MODELS[t1].values(): + for field in InternalHelper.MODELS[t1].values(): if field.get("required") and field["type"].startswith("relation"): ftable = ModelsHelper.get_foreign_table_from_to_or_reference( field.get("to"), field.get("reference") @@ -1516,15 +1526,13 @@ def main() -> None: Main entry point for this script to generate the schema_relational.sql from models.yml. """ - global MODELS - # Retrieve models.yml from call-parameter for testing purposes, local file or GitHub if len(sys.argv) > 1: file = sys.argv[1] else: file = str(SOURCE) - MODELS, checksum = InternalHelper.read_models_yml(file) + _, checksum = InternalHelper.read_models_yml(file) ( pre_code, diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 15a6555e..6de9ca21 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -90,9 +90,17 @@ def get_initial_letters(words: str) -> str: @staticmethod @max_length - def get_table_name(table_name: str) -> str: - """get's the table name as old collection name with appendix '_t'""" - if not table_name.endswith("_t"): + def get_table_name(table_name: str, migration: bool = False) -> str: + """ + Gets the table name as old collection name with suffix '_t'. + If migration is True '_m'. + Takes either collection or table name. + """ + if migration: + if table_name.endswith("_t"): + table_name = table_name[:-2] + return table_name + "_m" + elif not table_name.endswith("_t"): return table_name + "_t" else: return table_name @@ -100,13 +108,13 @@ def get_table_name(table_name: str) -> str: @staticmethod @max_length def get_view_name(table_name: str) -> str: - """get's the name of a view. Its the collection name in quotes""" + """gets the name of a view. Its the collection name in quotes""" return f'"{table_name}"' @staticmethod @max_length def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: - """get's the table name n:m-relations intermediate table""" + """gets the table name n:m-relations intermediate table""" if (f"{own.table}_{own.column}") < (f"{foreign.table}_{foreign.column}"): return f"nm_{own.table}_{HelperGetNames.get_initial_letters(own.column)}_{foreign.table}_t" else: @@ -115,7 +123,10 @@ def get_nm_table_name(own: TableFieldType, foreign: TableFieldType) -> str: @staticmethod @max_length def get_gm_table_name(table_field: TableFieldType) -> str: - """get's th table name for generic-list:many-relations intermediate table""" + """ + Gets the table name for generic-list:many-relations intermediate table + Does not deliver correct results on non-primary relations. + """ return f"gm_{table_field.table}_{table_field.column}_t" @staticmethod @@ -123,7 +134,7 @@ def get_gm_table_name(table_field: TableFieldType) -> str: def get_field_in_n_m_relation_list( own_table_field: TableFieldType, foreign_table_name: str ) -> str: - """get's the field name in a n:m-intermediate table. + """gets the field name in a n:m-intermediate table. If both sides of the relation are in same table, the field name without 's' is used, otherwise the related tables names are used """ diff --git a/models.yml b/models.yml index 347b97ad..65cbdf37 100644 --- a/models.yml +++ b/models.yml @@ -862,7 +862,7 @@ meeting: restriction_mode: C name: type: string - maxLength: 100 + maxLength: 200 default: OpenSlides restriction_mode: A required: true From 0f4dab180de94d637c3be4ed919c447ca36bd7c5 Mon Sep 17 00:00:00 2001 From: Raimund Renkert Date: Fri, 23 Jan 2026 11:26:14 +0100 Subject: [PATCH 130/142] Fix merge commit '68c9bc4' (#375) --- collections/organization.yml | 6 ++++++ dev/sql/schema_relational.sql | 4 +++- models.yml | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/collections/organization.yml b/collections/organization.yml index 858ddc4f..31da5568 100644 --- a/collections/organization.yml +++ b/collections/organization.yml @@ -32,6 +32,9 @@ gender_ids: disable_forward_with_attachments: type: boolean restriction_mode: A +restrict_edit_forward_committees: + type: boolean + restriction_mode: A # Configuration (only for the server owner) enable_electronic_voting: @@ -70,6 +73,9 @@ require_duplicate_from: enable_anonymous: type: boolean restriction_mode: A +restrict_editing_same_level_committee_admins: + type: boolean + restriction_mode: A # Saml settings saml_enabled: diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index beb45de9..e2b82abd 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '2ab51b5ebc2fc37f2202a887952b52a2' +-- MODELS_YML_CHECKSUM = 'fab7972b3adb110c4ad6d6d7557a595c' -- Function and meta table definitions @@ -937,6 +937,7 @@ CREATE TABLE organization_t ( login_text text, reset_password_verbose_errors boolean, disable_forward_with_attachments boolean, + restrict_edit_forward_committees boolean, enable_electronic_voting boolean, enable_chat boolean, limit_of_meetings integer CONSTRAINT minimum_limit_of_meetings CHECK (limit_of_meetings >= 0) DEFAULT 0, @@ -944,6 +945,7 @@ CREATE TABLE organization_t ( default_language varchar(256) CONSTRAINT enum_organization_default_language CHECK (default_language IN ('en', 'de', 'it', 'es', 'ru', 'cs', 'fr')) DEFAULT 'en', require_duplicate_from boolean, enable_anonymous boolean, + restrict_editing_same_level_committee_admins boolean, saml_enabled boolean, saml_login_button_text varchar(256) DEFAULT 'SAML login', saml_attr_mapping jsonb, diff --git a/models.yml b/models.yml index 65cbdf37..0509f970 100644 --- a/models.yml +++ b/models.yml @@ -3162,6 +3162,9 @@ organization: disable_forward_with_attachments: type: boolean restriction_mode: A + restrict_edit_forward_committees: + type: boolean + restriction_mode: A enable_electronic_voting: type: boolean restriction_mode: B @@ -3200,6 +3203,9 @@ organization: enable_anonymous: type: boolean restriction_mode: A + restrict_editing_same_level_committee_admins: + type: boolean + restriction_mode: A saml_enabled: type: boolean restriction_mode: A From fc4f3fe825a05a97d90549fae0df7644ca0a187e Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:16:17 +0100 Subject: [PATCH 131/142] [rel-DB] Add nt:ntR relation and n:m not_null trigger (#376) --- collections/meeting_user.yml | 1 + dev/sql/schema_relational.sql | 131 +++++++++++++++++++++-------- dev/src/generate_sql_schema.py | 149 ++++++++++++++++++++++++++++----- dev/src/helper_get_names.py | 2 + models.yml | 1 + 5 files changed, 230 insertions(+), 54 deletions(-) diff --git a/collections/meeting_user.yml b/collections/meeting_user.yml index 48ab29fd..841ea7b3 100644 --- a/collections/meeting_user.yml +++ b/collections/meeting_user.yml @@ -92,6 +92,7 @@ chat_message_ids: group_ids: type: relation-list to: group/meeting_user_ids + required: true equal_fields: meeting_id restriction_mode: A structure_level_ids: diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index e2b82abd..15fbbbb8 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'fab7972b3adb110c4ad6d6d7557a595c' +-- MODELS_YML_CHECKSUM = 'd6653826cc4577c311a9a9ad5668ac31' -- Function and meta table definitions @@ -210,7 +210,7 @@ BEGIN END; $not_null_trigger$ language plpgsql; -CREATE FUNCTION check_not_null_for_relation_lists() RETURNS trigger AS $not_null_trigger$ +CREATE FUNCTION check_not_null_for_1_n() RETURNS trigger AS $not_null_trigger$ -- usage with 3 parameters IN TRIGGER DEFINITION: -- table_name: relation to check, usually a view -- column_name: field to check, usually a field in a view @@ -250,6 +250,59 @@ BEGIN END; $not_null_trigger$ language plpgsql; +CREATE FUNCTION check_not_null_for_n_m() RETURNS trigger AS $not_null_trigger$ +-- Parameters required for both INSERT and DELETE operations +-- 0. intermediate_table_name – name of the n:m table +-- 1. own_collection – name of the table on which the trigger is defined +-- 2. own_column – column in `own_collection` referencing +-- `foreign_collection` +-- 3. intermediate_table_own_key – column in the n:m table referencing +-- `own_collection` +-- +-- Parameters needed for extended error message generation for 'DELETE' +-- (can be empty on INSERT) +-- 4. intermediate_table_foreign_key – column in the n:m table referencing +-- `foreign_collection` +-- 5. foreign_collection – name of the foreign table +-- 6. foreign_column – column in the foreign table referencing +-- `own_collection` +DECLARE + -- Always required + intermediate_table_name TEXT := TG_ARGV[0]; + own_collection TEXT := TG_ARGV[1]; + own_column TEXT := TG_ARGV[2]; + intermediate_table_own_key TEXT := TG_ARGV[3]; + + -- Only for TG_OP = 'DELETE' + intermediate_table_foreign_key TEXT := TG_ARGV[4]; + foreign_collection TEXT := TG_ARGV[5]; + foreign_column TEXT := TG_ARGV[6]; + + -- Calculated + own_id INTEGER; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT; +BEGIN + IF (TG_OP = 'INSERT') THEN + own_id := NEW.id; + ELSE + own_id := hstore(OLD) -> intermediate_table_own_key; + foreign_id := hstore(OLD) -> intermediate_table_foreign_key; + END IF; + + EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', intermediate_table_name, intermediate_table_own_key, own_id) INTO counted; + IF (counted is NULL) THEN + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); + IF (TG_OP = 'DELETE') THEN + error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); + END IF; + RAISE EXCEPTION '%', error_message; + END IF; + RETURN NULL; +END; +$not_null_trigger$ language plpgsql; + -- Type definitions @@ -2304,118 +2357,130 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_ --- Create triggers checking foreign_id not null for relation-lists +-- Create triggers checking foreign_id not null for 1:n relationships -- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_agenda_item_list_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); -- definition trigger not null for meeting.default_projector_topic_ids against projector_t.used_as_default_projector_for_topic_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_topic_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); -- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projector_t.used_as_default_projector_for_list_of_speakers_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_list_of_speakers_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); -- definition trigger not null for meeting.default_projector_current_los_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_los_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_current_los_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_los_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_ids against projector_t.used_as_default_projector_for_motion_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); -- definition trigger not null for meeting.default_projector_amendment_ids against projector_t.used_as_default_projector_for_amendment_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_amendment_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_block_ids against projector_t.used_as_default_projector_for_motion_block_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_block_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); -- definition trigger not null for meeting.default_projector_assignment_ids against projector_t.used_as_default_projector_for_assignment_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); -- definition trigger not null for meeting.default_projector_mediafile_ids against projector_t.used_as_default_projector_for_mediafile_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_mediafile_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); -- definition trigger not null for meeting.default_projector_message_ids against projector_t.used_as_default_projector_for_message_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_message_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); -- definition trigger not null for meeting.default_projector_countdown_ids against projector_t.used_as_default_projector_for_countdown_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_countdown_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); -- definition trigger not null for meeting.default_projector_assignment_poll_ids against projector_t.used_as_default_projector_for_assignment_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_poll_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); -- definition trigger not null for meeting.default_projector_motion_poll_ids against projector_t.used_as_default_projector_for_motion_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_poll_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); -- definition trigger not null for meeting.default_projector_poll_ids against projector_t.used_as_default_projector_for_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_poll_ids', ''); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); + + + + +-- Create triggers checking foreign_ids not null for n:m relationships + +-- definition trigger not null for meeting_user.group_ids against group.meeting_user_ids through nm_group_meeting_user_ids_meeting_user_t +CREATE CONSTRAINT TRIGGER tr_i_meeting_user_group_ids AFTER INSERT ON meeting_user_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user', 'group_ids', 'meeting_user_id'); + +CREATE CONSTRAINT TRIGGER tr_d_meeting_user_group_ids AFTER DELETE ON nm_group_meeting_user_ids_meeting_user_t INITIALLY DEFERRED +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user', 'group_ids', 'meeting_user_id', 'group_id', 'group', 'meeting_user_ids'); @@ -3290,7 +3355,7 @@ SQL nt:nGt => committee/organization_tag_ids:-> organization_tag/tagged_ids SQL nr:1r => gender/user_ids:-> user/gender_id -SQL nt:nt => group/meeting_user_ids:-> meeting_user/group_ids +SQL nt:ntR => group/meeting_user_ids:-> meeting_user/group_ids SQL 1t:1rR => group/default_group_for_meeting_id:-> meeting/default_group_id SQL 1t:1r => group/admin_group_for_meeting_id:-> meeting/admin_group_id SQL 1t:1r => group/anonymous_group_for_meeting_id:-> meeting/anonymous_group_id @@ -3454,7 +3519,7 @@ SQL nt:1r => meeting_user/assignment_candidate_ids:-> assignment_candidate/meeti FIELD 1r:nt => meeting_user/vote_delegated_to_id:-> meeting_user/vote_delegations_from_ids SQL nt:1r => meeting_user/vote_delegations_from_ids:-> meeting_user/vote_delegated_to_id SQL nt:1r => meeting_user/chat_message_ids:-> chat_message/meeting_user_id -SQL nt:nt => meeting_user/group_ids:-> group/meeting_user_ids +SQL ntR:nt => meeting_user/group_ids:-> group/meeting_user_ids SQL nt:nt => meeting_user/structure_level_ids:-> structure_level/meeting_user_ids FIELD 1r:nt => motion/lead_motion_id:-> motion/amendment_ids diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index 6ff69404..eeaba496 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -34,7 +34,8 @@ class SchemaZoneTexts(TypedDict, total=False): alter_table_final: str create_trigger_partitioned_sequences: str create_trigger_1_1_relation_not_null: str - create_trigger_relationlistnotnull: str + create_trigger_1_n_relation_not_null: str + create_trigger_n_m_relation_not_null: str create_trigger_unique_ids_pair_code: str create_trigger_notify: str undecided: str @@ -78,7 +79,7 @@ class GenerateCodeBlocks: def generate_the_code( cls, ) -> tuple[ - str, str, str, str, str, list[str], str, str, str, str, str, str, list[str] + str, str, str, str, str, list[str], str, str, str, str, str, str, str, list[str] ]: """ Return values: @@ -93,7 +94,8 @@ def generate_the_code( g:m-relations name schema: f"gm_{table_field.table}_{table_field.column}" of table with generic-list-field create_trigger_partitioned_sequences_code: Definitions of triggers calling generate_sequence create_trigger_1_1_relation_not_null_code: Definitions of triggers calling check_not_null_for_1_1_relation - create_trigger_relationlistnotnull_code: Definitions of triggers calling check_not_null_for_relation_lists + create_trigger_1_n_relation_not_null_code: Definitions of triggers calling check_not_null_for_1_n + create_trigger_n_m_relation_not_null_code: Definitions of triggers calling check_not_null_for_n_m create_trigger_unique_ids_pair_code: Definitions of triggers calling check_unique_ids_pair create_trigger_notify_code: Definitions of triggers calling notify_modified_models errors: to show @@ -125,7 +127,8 @@ def generate_the_code( alter_table_final_code: str = "" create_trigger_partitioned_sequences_code: str = "" create_trigger_1_1_relation_not_null_code: str = "" - create_trigger_relationlistnotnull_code: str = "" + create_trigger_1_n_relation_not_null_code: str = "" + create_trigger_n_m_relation_not_null_code: str = "" create_trigger_unique_ids_pair_code: str = "" create_trigger_notify_code: str = "" final_info_code: str = "" @@ -178,8 +181,10 @@ def generate_the_code( create_trigger_partitioned_sequences_code += code + "\n" if code := schema_zone_texts["create_trigger_1_1_relation_not_null"]: create_trigger_1_1_relation_not_null_code += code + "\n" - if code := schema_zone_texts["create_trigger_relationlistnotnull"]: - create_trigger_relationlistnotnull_code += code + "\n" + if code := schema_zone_texts["create_trigger_1_n_relation_not_null"]: + create_trigger_1_n_relation_not_null_code += code + "\n" + if code := schema_zone_texts["create_trigger_n_m_relation_not_null"]: + create_trigger_n_m_relation_not_null_code += code + "\n" if code := schema_zone_texts["create_trigger_unique_ids_pair_code"]: create_trigger_unique_ids_pair_code += code + "\n" if code := schema_zone_texts["final_info"]: @@ -208,7 +213,8 @@ def generate_the_code( im_table_code, create_trigger_partitioned_sequences_code, create_trigger_1_1_relation_not_null_code, - create_trigger_relationlistnotnull_code, + create_trigger_1_n_relation_not_null_code, + create_trigger_n_m_relation_not_null_code, create_trigger_unique_ids_pair_code, create_trigger_notify_code, errors, @@ -515,14 +521,23 @@ def get_relation_list_type( own_table_field.field_def == foreign_table_field.field_def, ) if own_table_field.field_def.get("required"): - text["create_trigger_relationlistnotnull"] = ( - cls.get_trigger_check_not_null_for_relation_lists( - own_table_field.table, - own_table_field.column, - foreign_table_field.table, - foreign_table_field.column, + if ( + type_ := foreign_table_field.field_def.get("type", "") + ) == "relation": + text["create_trigger_1_n_relation_not_null"] = ( + cls.get_trigger_check_not_null_for_1_n( + own_table_field.table, + own_table_field.column, + foreign_table_field.table, + foreign_table_field.column, + ) + ) + elif type_ == "relation-list": + text["create_trigger_n_m_relation_not_null"] = ( + cls.get_trigger_check_not_null_for_n_m( + own_table_field, foreign_table_field + ) ) - ) if ( own_table_field.table == foreign_table_field.table and own_table_field.column == foreign_table_field.column @@ -610,7 +625,7 @@ def get_trigger_check_not_null_for_1_1_relation( ) @classmethod - def get_trigger_check_not_null_for_relation_lists( + def get_trigger_check_not_null_for_1_n( cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str ) -> str: own_table_t = HelperGetNames.get_table_name(own_table) @@ -619,10 +634,40 @@ def get_trigger_check_not_null_for_relation_lists( f""" -- definition trigger not null for {own_table}.{own_column} against {foreign_table_t}.{foreign_column} CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', ''); + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table}', '{own_column}', ''); CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_relation_lists('{own_table}', '{own_column}', '{foreign_column}'); + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table}', '{own_column}', '{foreign_column}'); + + """ + ) + + @classmethod + def get_trigger_check_not_null_for_n_m( + cls, own_table_field: TableFieldType, foreign_table_field: TableFieldType + ) -> str: + own_table = own_table_field.table + own_column = own_table_field.column + own_table_t = HelperGetNames.get_table_name(own_table) + foreign_table = foreign_table_field.table + foreign_column = foreign_table_field.column + intermediate_table_name = HelperGetNames.get_nm_table_name( + own_table_field, foreign_table_field + ) + intermediate_table_own_key = HelperGetNames.get_field_in_n_m_relation_list( + own_table_field, foreign_table_field.table + ) + intermediate_table_foreign_key = HelperGetNames.get_field_in_n_m_relation_list( + foreign_table_field, own_table_field.table + ) + return dedent( + f""" + -- definition trigger not null for {own_table}.{own_column} against {foreign_table}.{foreign_column} through {intermediate_table_name} + CREATE CONSTRAINT TRIGGER tr_i_{own_table}_{own_column} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('{intermediate_table_name}', '{own_table}', '{own_column}', '{intermediate_table_own_key}'); + + CREATE CONSTRAINT TRIGGER tr_d_{own_table}_{own_column} AFTER DELETE ON {intermediate_table_name} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('{intermediate_table_name}', '{own_table}', '{own_column}', '{intermediate_table_own_key}', '{intermediate_table_foreign_key}', '{foreign_table}', '{foreign_column}'); """ ) @@ -930,7 +975,7 @@ class Helper: for type_, field_check in { "1_1": "%I", - "relation_lists": "array_length(%I, 1)", + "1_n": "array_length(%I, 1)", }.items(): FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( f""" @@ -976,6 +1021,63 @@ class Helper: """ ) + FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( + """ + CREATE FUNCTION check_not_null_for_n_m() RETURNS trigger AS $not_null_trigger$ + -- Parameters required for both INSERT and DELETE operations + -- 0. intermediate_table_name – name of the n:m table + -- 1. own_collection – name of the table on which the trigger is defined + -- 2. own_column – column in `own_collection` referencing + -- `foreign_collection` + -- 3. intermediate_table_own_key – column in the n:m table referencing + -- `own_collection` + -- + -- Parameters needed for extended error message generation for 'DELETE' + -- (can be empty on INSERT) + -- 4. intermediate_table_foreign_key – column in the n:m table referencing + -- `foreign_collection` + -- 5. foreign_collection – name of the foreign table + -- 6. foreign_column – column in the foreign table referencing + -- `own_collection` + DECLARE + -- Always required + intermediate_table_name TEXT := TG_ARGV[0]; + own_collection TEXT := TG_ARGV[1]; + own_column TEXT := TG_ARGV[2]; + intermediate_table_own_key TEXT := TG_ARGV[3]; + + -- Only for TG_OP = 'DELETE' + intermediate_table_foreign_key TEXT := TG_ARGV[4]; + foreign_collection TEXT := TG_ARGV[5]; + foreign_column TEXT := TG_ARGV[6]; + + -- Calculated + own_id INTEGER; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT; + BEGIN + IF (TG_OP = 'INSERT') THEN + own_id := NEW.id; + ELSE + own_id := hstore(OLD) -> intermediate_table_own_key; + foreign_id := hstore(OLD) -> intermediate_table_foreign_key; + END IF; + + EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', intermediate_table_name, intermediate_table_own_key, own_id) INTO counted; + IF (counted is NULL) THEN + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); + IF (TG_OP = 'DELETE') THEN + error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); + END IF; + RAISE EXCEPTION '%', error_message; + END IF; + RETURN NULL; + END; + $not_null_trigger$ language plpgsql; + """ + ) + FIELD_TEMPLATE = string.Template( " ${field_name} ${type}${primary_key}${required}${unique}${check_enum}${minimum}${minLength}${default},\n" ) @@ -1544,7 +1646,8 @@ def main() -> None: im_table_code, create_trigger_partitioned_sequences_code, create_trigger_1_1_relation_not_null_code, - create_trigger_relationlistnotnull_code, + create_trigger_1_n_relation_not_null_code, + create_trigger_n_m_relation_not_null_code, create_trigger_unique_ids_pair_code, create_trigger_notify_code, errors, @@ -1571,9 +1674,13 @@ def main() -> None: ) dest.write(create_trigger_1_1_relation_not_null_code) dest.write( - "\n\n-- Create triggers checking foreign_id not null for relation-lists\n" + "\n\n-- Create triggers checking foreign_id not null for 1:n relationships\n" + ) + dest.write(create_trigger_1_n_relation_not_null_code) + dest.write( + "\n\n-- Create triggers checking foreign_ids not null for n:m relationships\n" ) - dest.write(create_trigger_relationlistnotnull_code) + dest.write(create_trigger_n_m_relation_not_null_code) dest.write( "\n\n-- Create triggers preventing mirrored duplicates in fields referencing themselves\n" ) diff --git a/dev/src/helper_get_names.py b/dev/src/helper_get_names.py index 6de9ca21..bbc1e9b0 100644 --- a/dev/src/helper_get_names.py +++ b/dev/src/helper_get_names.py @@ -459,7 +459,9 @@ def generate_field_or_sql_decision( ("nt", "1rR"): (FieldSqlErrorType.SQL, False), ("nt", "nGt"): (FieldSqlErrorType.SQL, False), ("nt", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), + ("nt", "ntR"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), ("ntR", "1r"): (FieldSqlErrorType.SQL, False), + ("ntR", "nt"): (FieldSqlErrorType.SQL, "primary_decide_alphabetical"), ("nts", "nts"): (FieldSqlErrorType.SQL, False), } diff --git a/models.yml b/models.yml index 0509f970..650d6bf5 100644 --- a/models.yml +++ b/models.yml @@ -2228,6 +2228,7 @@ meeting_user: group_ids: type: relation-list to: group/meeting_user_ids + required: true equal_fields: meeting_id restriction_mode: A structure_level_ids: From 2c58ca56c6ea2e436acdf6dabd0c64e9ac35c173 Mon Sep 17 00:00:00 2001 From: Raimund Renkert Date: Mon, 2 Feb 2026 15:58:06 +0100 Subject: [PATCH 132/142] [rel-db] Add CREATE INDEX and update custom SQLs (#378) --- collections/committee.yml | 1 - collections/meeting.yml | 1 - collections/user.yml | 23 ++-- dev/sql/schema_relational.sql | 244 +++++++++++++++++++++++++++++++-- dev/src/generate_sql_schema.py | 24 ++-- models.yml | 28 ++-- 6 files changed, 265 insertions(+), 56 deletions(-) diff --git a/collections/committee.yml b/collections/committee.yml index a21ac2ec..9055bafe 100644 --- a/collections/committee.yml +++ b/collections/committee.yml @@ -39,7 +39,6 @@ user_ids: SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id UNION diff --git a/collections/meeting.yml b/collections/meeting.yml index eba23580..0cf84141 100644 --- a/collections/meeting.yml +++ b/collections/meeting.yml @@ -1091,7 +1091,6 @@ user_ids: ( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids to: user/meeting_ids diff --git a/collections/user.yml b/collections/user.yml index af702627..f0cb845d 100644 --- a/collections/user.yml +++ b/collections/user.yml @@ -100,15 +100,15 @@ committee_ids: description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" sql: | ( - SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL) FROM ( -- Select committee_ids from meetings the user is part of SELECT m.committee_id - FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - INNER JOIN meeting_t AS m ON m.id = mu.meeting_id - WHERE mu.user_id = u.id - + FROM user_t u + INNER JOIN meeting_user_t mu ON u.id = mu.user_id + INNER JOIN meeting_t m ON mu.meeting_id = m.id + WHERE u.id = u.id + UNION -- Select committee_ids from committee managers @@ -119,11 +119,11 @@ committee_ids: UNION -- Select home_committee_id from user - SELECT u.home_committee_id - WHERE u.home_committee_id IS NOT NULL - ) _ + SELECT home_committee_id + FROM user_t + WHERE home_committee_id IS NOT NULL + ) AS committee_id ) AS committee_ids - # committee specific permissions committee_management_ids: type: relation-list @@ -176,13 +176,12 @@ history_entry_ids: meeting_ids: type: relation-list - description: Calculated. All ids from meetings calculated via meeting_user and group_ids as integers. + description: Calculated. All ids from meetings calculated via meeting_user. read_only: true sql: | ( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids to: meeting/user_ids diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 15fbbbb8..547553bd 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = 'd6653826cc4577c311a9a9ad5668ac31' +-- MODELS_YML_CHECKSUM = '9163d47c35714018ebeba7cd55b1f886' -- Function and meta table definitions @@ -1411,72 +1411,96 @@ CREATE TABLE nm_chat_group_read_group_ids_group_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); +CREATE INDEX ON nm_chat_group_read_group_ids_group_t (chat_group_id); +CREATE INDEX ON nm_chat_group_read_group_ids_group_t (group_id); CREATE TABLE nm_chat_group_write_group_ids_group_t ( chat_group_id integer NOT NULL REFERENCES chat_group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (chat_group_id, group_id) ); +CREATE INDEX ON nm_chat_group_write_group_ids_group_t (chat_group_id); +CREATE INDEX ON nm_chat_group_write_group_ids_group_t (group_id); CREATE TABLE nm_committee_manager_ids_user_t ( committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (committee_id, user_id) ); +CREATE INDEX ON nm_committee_manager_ids_user_t (committee_id); +CREATE INDEX ON nm_committee_manager_ids_user_t (user_id); CREATE TABLE nm_committee_all_child_ids_committee_t ( all_child_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, all_parent_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (all_child_id, all_parent_id) ); +CREATE INDEX ON nm_committee_all_child_ids_committee_t (all_child_id); +CREATE INDEX ON nm_committee_all_child_ids_committee_t (all_parent_id); CREATE TABLE nm_committee_forward_to_committee_ids_committee_t ( forward_to_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, receive_forwardings_from_committee_id integer NOT NULL REFERENCES committee_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (forward_to_committee_id, receive_forwardings_from_committee_id) ); +CREATE INDEX ON nm_committee_forward_to_committee_ids_committee_t (forward_to_committee_id); +CREATE INDEX ON nm_committee_forward_to_committee_ids_committee_t (receive_forwardings_from_committee_id); CREATE TABLE nm_group_meeting_user_ids_meeting_user_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_user_id) ); +CREATE INDEX ON nm_group_meeting_user_ids_meeting_user_t (group_id); +CREATE INDEX ON nm_group_meeting_user_ids_meeting_user_t (meeting_user_id); CREATE TABLE nm_group_mmagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); +CREATE INDEX ON nm_group_mmagi_meeting_mediafile_t (group_id); +CREATE INDEX ON nm_group_mmagi_meeting_mediafile_t (meeting_mediafile_id); CREATE TABLE nm_group_mmiagi_meeting_mediafile_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, meeting_mediafile_id) ); +CREATE INDEX ON nm_group_mmiagi_meeting_mediafile_t (group_id); +CREATE INDEX ON nm_group_mmiagi_meeting_mediafile_t (meeting_mediafile_id); CREATE TABLE nm_group_read_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); +CREATE INDEX ON nm_group_read_comment_section_ids_motion_comment_section_t (group_id); +CREATE INDEX ON nm_group_read_comment_section_ids_motion_comment_section_t (motion_comment_section_id); CREATE TABLE nm_group_write_comment_section_ids_motion_comment_section_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, motion_comment_section_id integer NOT NULL REFERENCES motion_comment_section_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, motion_comment_section_id) ); +CREATE INDEX ON nm_group_write_comment_section_ids_motion_comment_section_t (group_id); +CREATE INDEX ON nm_group_write_comment_section_ids_motion_comment_section_t (motion_comment_section_id); CREATE TABLE nm_group_poll_ids_poll_t ( group_id integer NOT NULL REFERENCES group_t (id) ON DELETE CASCADE INITIALLY DEFERRED, poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (group_id, poll_id) ); +CREATE INDEX ON nm_group_poll_ids_poll_t (group_id); +CREATE INDEX ON nm_group_poll_ids_poll_t (poll_id); CREATE TABLE nm_meeting_present_user_ids_user_t ( meeting_id integer NOT NULL REFERENCES meeting_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_id, user_id) ); +CREATE INDEX ON nm_meeting_present_user_ids_user_t (meeting_id); +CREATE INDEX ON nm_meeting_present_user_ids_user_t (user_id); CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( meeting_mediafile_id integer NOT NULL REFERENCES meeting_mediafile_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1487,24 +1511,32 @@ CREATE TABLE gm_meeting_mediafile_attachment_ids_t ( CONSTRAINT valid_attachment_id_part1 CHECK (split_part(attachment_id, '/', 1) IN ('motion', 'topic', 'assignment')), CONSTRAINT unique_$meeting_mediafile_id_$attachment_id UNIQUE (meeting_mediafile_id, attachment_id) ); +CREATE INDEX ON gm_meeting_mediafile_attachment_ids_t (meeting_mediafile_id); +CREATE INDEX ON gm_meeting_mediafile_attachment_ids_t (attachment_id); CREATE TABLE nm_meeting_user_structure_level_ids_structure_level_t ( meeting_user_id integer NOT NULL REFERENCES meeting_user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, structure_level_id integer NOT NULL REFERENCES structure_level_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (meeting_user_id, structure_level_id) ); +CREATE INDEX ON nm_meeting_user_structure_level_ids_structure_level_t (meeting_user_id); +CREATE INDEX ON nm_meeting_user_structure_level_ids_structure_level_t (structure_level_id); CREATE TABLE nm_motion_all_derived_motion_ids_motion_t ( all_derived_motion_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, all_origin_id integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (all_derived_motion_id, all_origin_id) ); +CREATE INDEX ON nm_motion_all_derived_motion_ids_motion_t (all_derived_motion_id); +CREATE INDEX ON nm_motion_all_derived_motion_ids_motion_t (all_origin_id); CREATE TABLE nm_motion_identical_motion_ids_motion_t ( identical_motion_id_1 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, identical_motion_id_2 integer NOT NULL REFERENCES motion_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (identical_motion_id_1, identical_motion_id_2) ); +CREATE INDEX ON nm_motion_identical_motion_ids_motion_t (identical_motion_id_1); +CREATE INDEX ON nm_motion_identical_motion_ids_motion_t (identical_motion_id_2); CREATE TABLE gm_motion_state_extension_reference_ids_t ( motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1513,6 +1545,8 @@ CREATE TABLE gm_motion_state_extension_reference_ids_t ( CONSTRAINT valid_state_extension_reference_id_part1 CHECK (split_part(state_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$state_extension_reference_id UNIQUE (motion_id, state_extension_reference_id) ); +CREATE INDEX ON gm_motion_state_extension_reference_ids_t (motion_id); +CREATE INDEX ON gm_motion_state_extension_reference_ids_t (state_extension_reference_id); CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( motion_id integer NOT NULL REFERENCES motion_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1521,12 +1555,16 @@ CREATE TABLE gm_motion_recommendation_extension_reference_ids_t ( CONSTRAINT valid_recommendation_extension_reference_id_part1 CHECK (split_part(recommendation_extension_reference_id, '/', 1) IN ('motion')), CONSTRAINT unique_$motion_id_$recommendation_extension_reference_id UNIQUE (motion_id, recommendation_extension_reference_id) ); +CREATE INDEX ON gm_motion_recommendation_extension_reference_ids_t (motion_id); +CREATE INDEX ON gm_motion_recommendation_extension_reference_ids_t (recommendation_extension_reference_id); CREATE TABLE nm_motion_state_next_state_ids_motion_state_t ( next_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, previous_state_id integer NOT NULL REFERENCES motion_state_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (next_state_id, previous_state_id) ); +CREATE INDEX ON nm_motion_state_next_state_ids_motion_state_t (next_state_id); +CREATE INDEX ON nm_motion_state_next_state_ids_motion_state_t (previous_state_id); CREATE TABLE gm_organization_tag_tagged_ids_t ( organization_tag_id integer NOT NULL REFERENCES organization_tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1536,12 +1574,16 @@ CREATE TABLE gm_organization_tag_tagged_ids_t ( CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('committee', 'meeting')), CONSTRAINT unique_$organization_tag_id_$tagged_id UNIQUE (organization_tag_id, tagged_id) ); +CREATE INDEX ON gm_organization_tag_tagged_ids_t (organization_tag_id); +CREATE INDEX ON gm_organization_tag_tagged_ids_t (tagged_id); CREATE TABLE nm_poll_voted_ids_user_t ( poll_id integer NOT NULL REFERENCES poll_t (id) ON DELETE CASCADE INITIALLY DEFERRED, user_id integer NOT NULL REFERENCES user_t (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (poll_id, user_id) ); +CREATE INDEX ON nm_poll_voted_ids_user_t (poll_id); +CREATE INDEX ON nm_poll_voted_ids_user_t (user_id); CREATE TABLE gm_tag_tagged_ids_t ( tag_id integer NOT NULL REFERENCES tag_t(id) ON DELETE CASCADE INITIALLY DEFERRED, @@ -1552,6 +1594,8 @@ CREATE TABLE gm_tag_tagged_ids_t ( CONSTRAINT valid_tagged_id_part1 CHECK (split_part(tagged_id, '/', 1) IN ('agenda_item', 'assignment', 'motion')), CONSTRAINT unique_$tag_id_$tagged_id UNIQUE (tag_id, tagged_id) ); +CREATE INDEX ON gm_tag_tagged_ids_t (tag_id); +CREATE INDEX ON gm_tag_tagged_ids_t (tagged_id); -- View definitions @@ -1600,7 +1644,6 @@ CREATE VIEW "committee" AS SELECT *, SELECT mu.user_id FROM meeting_t AS m INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE m.committee_id = c.id UNION @@ -1724,7 +1767,6 @@ CREATE VIEW "meeting" AS SELECT *, ( SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.meeting_id = m.id ) AS user_ids , @@ -1984,15 +2026,15 @@ FROM topic_t t; CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, ( - SELECT array_agg(DISTINCT committee_id ORDER BY committee_id) + SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL) FROM ( -- Select committee_ids from meetings the user is part of SELECT m.committee_id - FROM meeting_user_t AS mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id - INNER JOIN meeting_t AS m ON m.id = mu.meeting_id - WHERE mu.user_id = u.id - + FROM user_t u + INNER JOIN meeting_user_t mu ON u.id = mu.user_id + INNER JOIN meeting_t m ON mu.meeting_id = m.id + WHERE u.id = u.id + UNION -- Select committee_ids from committee managers @@ -2003,9 +2045,10 @@ CREATE VIEW "user" AS SELECT *, UNION -- Select home_committee_id from user - SELECT u.home_committee_id - WHERE u.home_committee_id IS NOT NULL - ) _ + SELECT home_committee_id + FROM user_t + WHERE home_committee_id IS NOT NULL + ) AS committee_id ) AS committee_ids , (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, @@ -2020,14 +2063,13 @@ CREATE VIEW "user" AS SELECT *, ( SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id) FROM meeting_user_t mu - INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id WHERE mu.user_id = u.id ) AS meeting_ids FROM user_t u; comment on column "user".committee_ids is 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting'; -comment on column "user".meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user and group_ids as integers.'; +comment on column "user".meeting_ids is 'Calculated. All ids from meetings calculated via meeting_user.'; CREATE VIEW "vote" AS SELECT * FROM vote_t v; @@ -2035,222 +2077,396 @@ CREATE VIEW "vote" AS SELECT * FROM vote_t v; -- Alter table relations ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (content_object_id_motion_id); ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (content_object_id_motion_block_id); ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (content_object_id_assignment_id); ALTER TABLE agenda_item_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (content_object_id_topic_id); ALTER TABLE agenda_item_t ADD FOREIGN KEY(parent_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (parent_id); ALTER TABLE agenda_item_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON agenda_item_t (meeting_id); ALTER TABLE assignment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON assignment_t (meeting_id); ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON assignment_candidate_t (assignment_id); ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON assignment_candidate_t (meeting_user_id); ALTER TABLE assignment_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON assignment_candidate_t (meeting_id); ALTER TABLE chat_group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON chat_group_t (meeting_id); ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON chat_message_t (meeting_user_id); ALTER TABLE chat_message_t ADD FOREIGN KEY(chat_group_id) REFERENCES chat_group_t(id) INITIALLY DEFERRED; +CREATE INDEX ON chat_message_t (chat_group_id); ALTER TABLE chat_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON chat_message_t (meeting_id); ALTER TABLE committee_t ADD FOREIGN KEY(default_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON committee_t (default_meeting_id); ALTER TABLE committee_t ADD FOREIGN KEY(parent_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +CREATE INDEX ON committee_t (parent_id); ALTER TABLE group_t ADD FOREIGN KEY(used_as_motion_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON group_t (used_as_motion_poll_default_id); ALTER TABLE group_t ADD FOREIGN KEY(used_as_assignment_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON group_t (used_as_assignment_poll_default_id); ALTER TABLE group_t ADD FOREIGN KEY(used_as_topic_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON group_t (used_as_topic_poll_default_id); ALTER TABLE group_t ADD FOREIGN KEY(used_as_poll_default_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON group_t (used_as_poll_default_id); ALTER TABLE group_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON group_t (meeting_id); ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_entry_t (model_id_user_id); ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_entry_t (model_id_motion_id); ALTER TABLE history_entry_t ADD FOREIGN KEY(model_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_entry_t (model_id_assignment_id); ALTER TABLE history_entry_t ADD FOREIGN KEY(position_id) REFERENCES history_position_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_entry_t (position_id); ALTER TABLE history_entry_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_entry_t (meeting_id); ALTER TABLE history_position_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON history_position_t (user_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (content_object_id_motion_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (content_object_id_motion_block_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (content_object_id_assignment_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (content_object_id_topic_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (content_object_id_meeting_mediafile_id); ALTER TABLE list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON list_of_speakers_t (meeting_id); ALTER TABLE mediafile_t ADD FOREIGN KEY(published_to_meetings_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +CREATE INDEX ON mediafile_t (published_to_meetings_in_organization_id); ALTER TABLE mediafile_t ADD FOREIGN KEY(parent_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON mediafile_t (parent_id); ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON mediafile_t (owner_id_meeting_id); ALTER TABLE mediafile_t ADD FOREIGN KEY(owner_id_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +CREATE INDEX ON mediafile_t (owner_id_organization_id); ALTER TABLE meeting_t ADD FOREIGN KEY(is_active_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (is_active_in_organization_id); ALTER TABLE meeting_t ADD FOREIGN KEY(is_archived_in_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (is_archived_in_organization_id); ALTER TABLE meeting_t ADD FOREIGN KEY(template_for_organization_id) REFERENCES organization_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (template_for_organization_id); ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (motions_default_workflow_id); ALTER TABLE meeting_t ADD FOREIGN KEY(motions_default_amendment_workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (motions_default_amendment_workflow_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_main_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_projector_main_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_projector_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_projector_header_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_web_header_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_web_header_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_l_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_pdf_header_l_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_header_r_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_pdf_header_r_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_l_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_pdf_footer_l_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_footer_r_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_pdf_footer_r_id); ALTER TABLE meeting_t ADD FOREIGN KEY(logo_pdf_ballot_paper_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (logo_pdf_ballot_paper_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_regular_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_regular_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_italic_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_italic_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_bold_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_bold_italic_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_bold_italic_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_monospace_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_monospace_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_chyron_speaker_name_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_chyron_speaker_name_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h1_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_projector_h1_id); ALTER TABLE meeting_t ADD FOREIGN KEY(font_projector_h2_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (font_projector_h2_id); ALTER TABLE meeting_t ADD FOREIGN KEY(committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (committee_id); ALTER TABLE meeting_t ADD FOREIGN KEY(reference_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (reference_projector_id); ALTER TABLE meeting_t ADD FOREIGN KEY(list_of_speakers_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (list_of_speakers_countdown_id); ALTER TABLE meeting_t ADD FOREIGN KEY(poll_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (poll_countdown_id); ALTER TABLE meeting_t ADD FOREIGN KEY(default_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (default_group_id); ALTER TABLE meeting_t ADD FOREIGN KEY(admin_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (admin_group_id); ALTER TABLE meeting_t ADD FOREIGN KEY(anonymous_group_id) REFERENCES group_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_t (anonymous_group_id); ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(mediafile_id) REFERENCES mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_mediafile_t (mediafile_id); ALTER TABLE meeting_mediafile_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_mediafile_t (meeting_id); ALTER TABLE meeting_user_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_user_t (user_id); ALTER TABLE meeting_user_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_user_t (meeting_id); ALTER TABLE meeting_user_t ADD FOREIGN KEY(vote_delegated_to_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON meeting_user_t (vote_delegated_to_id); ALTER TABLE motion_t ADD FOREIGN KEY(lead_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (lead_motion_id); ALTER TABLE motion_t ADD FOREIGN KEY(sort_parent_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (sort_parent_id); ALTER TABLE motion_t ADD FOREIGN KEY(origin_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (origin_id); ALTER TABLE motion_t ADD FOREIGN KEY(origin_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (origin_meeting_id); ALTER TABLE motion_t ADD FOREIGN KEY(state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (state_id); ALTER TABLE motion_t ADD FOREIGN KEY(recommendation_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (recommendation_id); ALTER TABLE motion_t ADD FOREIGN KEY(category_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (category_id); ALTER TABLE motion_t ADD FOREIGN KEY(block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (block_id); ALTER TABLE motion_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_t (meeting_id); ALTER TABLE motion_block_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_block_t (meeting_id); ALTER TABLE motion_category_t ADD FOREIGN KEY(parent_id) REFERENCES motion_category_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_category_t (parent_id); ALTER TABLE motion_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_category_t (meeting_id); ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_change_recommendation_t (motion_id); ALTER TABLE motion_change_recommendation_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_change_recommendation_t (meeting_id); ALTER TABLE motion_comment_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_comment_t (motion_id); ALTER TABLE motion_comment_t ADD FOREIGN KEY(section_id) REFERENCES motion_comment_section_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_comment_t (section_id); ALTER TABLE motion_comment_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_comment_t (meeting_id); ALTER TABLE motion_comment_section_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_comment_section_t (meeting_id); ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_editor_t (meeting_user_id); ALTER TABLE motion_editor_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_editor_t (motion_id); ALTER TABLE motion_editor_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_editor_t (meeting_id); ALTER TABLE motion_state_t ADD FOREIGN KEY(submitter_withdraw_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_state_t (submitter_withdraw_state_id); ALTER TABLE motion_state_t ADD FOREIGN KEY(workflow_id) REFERENCES motion_workflow_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_state_t (workflow_id); ALTER TABLE motion_state_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_state_t (meeting_id); ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_submitter_t (meeting_user_id); ALTER TABLE motion_submitter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_submitter_t (motion_id); ALTER TABLE motion_submitter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_submitter_t (meeting_id); ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_supporter_t (meeting_user_id); ALTER TABLE motion_supporter_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_supporter_t (motion_id); ALTER TABLE motion_supporter_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_supporter_t (meeting_id); ALTER TABLE motion_workflow_t ADD FOREIGN KEY(first_state_id) REFERENCES motion_state_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_workflow_t (first_state_id); ALTER TABLE motion_workflow_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_workflow_t (meeting_id); ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_working_group_speaker_t (meeting_user_id); ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_working_group_speaker_t (motion_id); ALTER TABLE motion_working_group_speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON motion_working_group_speaker_t (meeting_id); ALTER TABLE option_t ADD FOREIGN KEY(poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (poll_id); ALTER TABLE option_t ADD FOREIGN KEY(used_as_global_option_in_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (used_as_global_option_in_poll_id); ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (content_object_id_motion_id); ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (content_object_id_user_id); ALTER TABLE option_t ADD FOREIGN KEY(content_object_id_poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (content_object_id_poll_candidate_list_id); ALTER TABLE option_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON option_t (meeting_id); ALTER TABLE organization_t ADD FOREIGN KEY(theme_id) REFERENCES theme_t(id) INITIALLY DEFERRED; +CREATE INDEX ON organization_t (theme_id); ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON personal_note_t (meeting_user_id); ALTER TABLE personal_note_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON personal_note_t (content_object_id_motion_id); ALTER TABLE personal_note_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON personal_note_t (meeting_id); ALTER TABLE point_of_order_category_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON point_of_order_category_t (meeting_id); ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_t (content_object_id_motion_id); ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_t (content_object_id_assignment_id); ALTER TABLE poll_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_t (content_object_id_topic_id); ALTER TABLE poll_t ADD FOREIGN KEY(global_option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_t (global_option_id); ALTER TABLE poll_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_t (meeting_id); ALTER TABLE poll_candidate_t ADD FOREIGN KEY(poll_candidate_list_id) REFERENCES poll_candidate_list_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_candidate_t (poll_candidate_list_id); ALTER TABLE poll_candidate_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_candidate_t (user_id); ALTER TABLE poll_candidate_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_candidate_t (meeting_id); ALTER TABLE poll_candidate_list_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON poll_candidate_list_t (meeting_id); ALTER TABLE projection_t ADD FOREIGN KEY(current_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (current_projector_id); ALTER TABLE projection_t ADD FOREIGN KEY(preview_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (preview_projector_id); ALTER TABLE projection_t ADD FOREIGN KEY(history_projector_id) REFERENCES projector_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (history_projector_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_meeting_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_id) REFERENCES motion_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_motion_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_meeting_mediafile_id) REFERENCES meeting_mediafile_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_meeting_mediafile_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_list_of_speakers_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_motion_block_id) REFERENCES motion_block_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_motion_block_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_assignment_id) REFERENCES assignment_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_assignment_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_agenda_item_id) REFERENCES agenda_item_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_agenda_item_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_topic_id) REFERENCES topic_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_topic_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_poll_id) REFERENCES poll_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_poll_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_message_id) REFERENCES projector_message_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_projector_message_id); ALTER TABLE projection_t ADD FOREIGN KEY(content_object_id_projector_countdown_id) REFERENCES projector_countdown_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (content_object_id_projector_countdown_id); ALTER TABLE projection_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projection_t (meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_agenda_item_list_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_agenda_item_list_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_topic_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_topic_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_list_of_speakers_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_list_of_speakers_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_current_los_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_current_los_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_motion_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_amendment_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_amendment_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_block_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_motion_block_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_assignment_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_assignment_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_mediafile_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_mediafile_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_message_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_message_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_countdown_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_countdown_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_assignment_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_assignment_poll_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_motion_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_motion_poll_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(used_as_default_projector_for_poll_in_meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (used_as_default_projector_for_poll_in_meeting_id); ALTER TABLE projector_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_t (meeting_id); ALTER TABLE projector_countdown_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_countdown_t (meeting_id); ALTER TABLE projector_message_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON projector_message_t (meeting_id); ALTER TABLE speaker_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +CREATE INDEX ON speaker_t (list_of_speakers_id); ALTER TABLE speaker_t ADD FOREIGN KEY(structure_level_list_of_speakers_id) REFERENCES structure_level_list_of_speakers_t(id) INITIALLY DEFERRED; +CREATE INDEX ON speaker_t (structure_level_list_of_speakers_id); ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_user_id) REFERENCES meeting_user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON speaker_t (meeting_user_id); ALTER TABLE speaker_t ADD FOREIGN KEY(point_of_order_category_id) REFERENCES point_of_order_category_t(id) INITIALLY DEFERRED; +CREATE INDEX ON speaker_t (point_of_order_category_id); ALTER TABLE speaker_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON speaker_t (meeting_id); ALTER TABLE structure_level_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON structure_level_t (meeting_id); ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(structure_level_id) REFERENCES structure_level_t(id) INITIALLY DEFERRED; +CREATE INDEX ON structure_level_list_of_speakers_t (structure_level_id); ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(list_of_speakers_id) REFERENCES list_of_speakers_t(id) INITIALLY DEFERRED; +CREATE INDEX ON structure_level_list_of_speakers_t (list_of_speakers_id); ALTER TABLE structure_level_list_of_speakers_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON structure_level_list_of_speakers_t (meeting_id); ALTER TABLE tag_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON tag_t (meeting_id); ALTER TABLE topic_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON topic_t (meeting_id); ALTER TABLE user_t ADD FOREIGN KEY(gender_id) REFERENCES gender_t(id) INITIALLY DEFERRED; +CREATE INDEX ON user_t (gender_id); ALTER TABLE user_t ADD FOREIGN KEY(home_committee_id) REFERENCES committee_t(id) INITIALLY DEFERRED; +CREATE INDEX ON user_t (home_committee_id); ALTER TABLE vote_t ADD FOREIGN KEY(option_id) REFERENCES option_t(id) INITIALLY DEFERRED; +CREATE INDEX ON vote_t (option_id); ALTER TABLE vote_t ADD FOREIGN KEY(user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON vote_t (user_id); ALTER TABLE vote_t ADD FOREIGN KEY(delegated_user_id) REFERENCES user_t(id) INITIALLY DEFERRED; +CREATE INDEX ON vote_t (delegated_user_id); ALTER TABLE vote_t ADD FOREIGN KEY(meeting_id) REFERENCES meeting_t(id) INITIALLY DEFERRED; +CREATE INDEX ON vote_t (meeting_id); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index eeaba496..f8509f55 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -1089,6 +1089,8 @@ class Helper: ${field2} integer NOT NULL REFERENCES ${table2} (id) ON DELETE CASCADE INITIALLY DEFERRED, PRIMARY KEY (${list_of_keys}) ); + CREATE INDEX ON ${table_name} (${field1}); + CREATE INDEX ON ${table_name} (${field2}); """ ) ) @@ -1102,6 +1104,8 @@ class Helper: CONSTRAINT ${valid_constraint_name} CHECK (split_part(${own_table_column}, '/', 1) IN ${tuple_of_foreign_table_names}), CONSTRAINT ${unique_constraint_name} UNIQUE (${own_table_name_with_ref_column}, ${own_table_column}) ); + CREATE INDEX ON ${table_name} (${own_table_name_with_ref_column}); + CREATE INDEX ON ${table_name} (${own_table_column}); """ ) ) @@ -1220,38 +1224,34 @@ def get_check_enum( def get_foreign_key_table_constraint_as_alter_table( table_name: str, foreign_table: str, - own_columns: list[str] | str, - fk_columns: list[str] | str, + own_column: str, + fk_column: str, initially_deferred: bool = False, delete_action: str = "", update_action: str = "", ) -> str: FOREIGN_KEY_TABLE_CONSTRAINT_TEMPLATE = string.Template( - "ALTER TABLE ${own_table} ADD FOREIGN KEY(${own_columns}) REFERENCES ${foreign_table}(${fk_columns})${initially_deferred}" + "ALTER TABLE ${own_table} ADD FOREIGN KEY(${own_column}) REFERENCES ${foreign_table}(${fk_column})${initially_deferred}${delete_action}${update_action};\n" + "CREATE INDEX ON ${own_table} (${own_column});\n" ) if initially_deferred: text_initially_deferred = " INITIALLY DEFERRED" else: text_initially_deferred = "" - if isinstance(own_columns, list): - own_columns = "(" + ", ".join(own_columns) + ")" - if isinstance(fk_columns, list): - fk_columns = "(" + ", ".join(fk_columns) + ")" own_table = HelperGetNames.get_table_name(table_name) foreign_table = HelperGetNames.get_table_name(foreign_table) result = FOREIGN_KEY_TABLE_CONSTRAINT_TEMPLATE.substitute( { "own_table": own_table, "foreign_table": foreign_table, - "own_columns": own_columns, - "fk_columns": fk_columns, + "own_column": own_column, + "fk_column": fk_column, "initially_deferred": text_initially_deferred, + "delete_action": Helper.get_on_action_mode(delete_action, True), + "update_action": Helper.get_on_action_mode(update_action, False), } ) - result += Helper.get_on_action_mode(delete_action, True) - result += Helper.get_on_action_mode(update_action, False) - result += ";\n" return result @staticmethod diff --git a/models.yml b/models.yml index 650d6bf5..4918a7b8 100644 --- a/models.yml +++ b/models.yml @@ -395,7 +395,6 @@ committee: sql: "(\n SELECT array_agg(DISTINCT user_id ORDER BY user_id)\n FROM (\n \ \ -- Select user_ids from committees meetings\n SELECT mu.user_id\n FROM\ \ meeting_t AS m\n INNER JOIN meeting_user_t AS mu ON mu.meeting_id = m.id\n\ - \ INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id = gmu.meeting_user_id\n\ \ WHERE m.committee_id = c.id\n\n UNION\n\n -- Select user_ids from\ \ committee managers\n SELECT cmu.user_id\n FROM nm_committee_manager_ids_user_t\ \ cmu\n WHERE cmu.committee_id = c.id\n\n UNION\n\n -- Select user_id\ @@ -1896,8 +1895,7 @@ meeting: meeting. read_only: true sql: "(\n SELECT array_agg(DISTINCT mu.user_id ORDER BY mu.user_id)\n FROM meeting_user_t\ - \ mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t AS gmu ON mu.id\ - \ = gmu.meeting_user_id\n WHERE mu.meeting_id = m.id\n) AS user_ids\n" + \ mu\n WHERE mu.meeting_id = m.id\n) AS user_ids\n" to: user/meeting_ids restriction_mode: A reference_projector_id: @@ -4485,15 +4483,15 @@ user: read_only: true description: 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting' - sql: "(\n SELECT array_agg(DISTINCT committee_id ORDER BY committee_id)\n FROM\ - \ (\n -- Select committee_ids from meetings the user is part of\n SELECT\ - \ m.committee_id\n FROM meeting_user_t AS mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t\ - \ AS gmu ON mu.id = gmu.meeting_user_id\n INNER JOIN meeting_t AS m ON m.id\ - \ = mu.meeting_id\n WHERE mu.user_id = u.id\n\n UNION\n\n -- Select\ - \ committee_ids from committee managers\n SELECT cmu.committee_id\n FROM\ - \ nm_committee_manager_ids_user_t cmu\n WHERE cmu.user_id = u.id\n\n UNION\n\ - \n -- Select home_committee_id from user\n SELECT u.home_committee_id\n\ - \ WHERE u.home_committee_id IS NOT NULL\n ) _\n) AS committee_ids\n" + sql: "(\n SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL)\n\ + \ FROM (\n -- Select committee_ids from meetings the user is part of\n \ + \ SELECT m.committee_id\n FROM user_t u\n INNER JOIN meeting_user_t\ + \ mu ON u.id = mu.user_id\n INNER JOIN meeting_t m ON mu.meeting_id =\ + \ m.id\n WHERE u.id = u.id\n \n UNION\n\n -- Select committee_ids\ + \ from committee managers\n SELECT cmu.committee_id\n FROM nm_committee_manager_ids_user_t\ + \ cmu\n WHERE cmu.user_id = u.id\n\n UNION\n\n -- Select home_committee_id\ + \ from user\n SELECT home_committee_id\n FROM user_t\n WHERE home_committee_id\ + \ IS NOT NULL\n ) AS committee_id\n) AS committee_ids\n" committee_management_ids: type: relation-list to: committee/manager_ids @@ -4542,12 +4540,10 @@ user: restriction_mode: A meeting_ids: type: relation-list - description: Calculated. All ids from meetings calculated via meeting_user and - group_ids as integers. + description: Calculated. All ids from meetings calculated via meeting_user. read_only: true sql: "(\n SELECT array_agg(DISTINCT mu.meeting_id ORDER BY mu.meeting_id)\n \ - \ FROM meeting_user_t mu\n INNER JOIN nm_group_meeting_user_ids_meeting_user_t\ - \ AS gmu ON mu.id = gmu.meeting_user_id\n WHERE mu.user_id = u.id\n) AS meeting_ids\n" + \ FROM meeting_user_t mu\n WHERE mu.user_id = u.id\n) AS meeting_ids\n" to: meeting/user_ids restriction_mode: E organization_id: From d0cc2af344dbca462824692fc5fd52929e725cf8 Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:40:36 +0100 Subject: [PATCH 133/142] [rel-DB] Fix check_not_null_for_n_m() (#377) --- collections/user.yml | 2 +- dev/sql/schema_relational.sql | 250 +++++++++++++----------- dev/src/generate_sql_schema.py | 340 +++++++++++++++++++++------------ models.yml | 4 +- 4 files changed, 359 insertions(+), 237 deletions(-) diff --git a/collections/user.yml b/collections/user.yml index f0cb845d..cbed5da9 100644 --- a/collections/user.yml +++ b/collections/user.yml @@ -108,7 +108,7 @@ committee_ids: INNER JOIN meeting_user_t mu ON u.id = mu.user_id INNER JOIN meeting_t m ON mu.meeting_id = m.id WHERE u.id = u.id - + UNION -- Select committee_ids from committee managers diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 547553bd..64a9d6ac 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '9163d47c35714018ebeba7cd55b1f886' +-- MODELS_YML_CHECKSUM = '70d875293a2d83c994af88245cd89ee5' -- Function and meta table definitions @@ -171,80 +171,104 @@ END; $read_only_trigger$ LANGUAGE plpgsql; CREATE FUNCTION check_not_null_for_1_1() RETURNS trigger AS $not_null_trigger$ --- usage with 3 parameters IN TRIGGER DEFINITION: --- table_name: relation to check, usually a view --- column_name: field to check, usually a field in a view --- foreign_key: field name of triggered table, that will be used to SELECT --- the values to check the not null. Can be empty on INSERT as then unused. +-- Parameters required for all operation types +-- 0. own_collection – name of the view on which the trigger is defined +-- 1. own_column – column in `own_table` referencing +-- `foreign_table` +-- +-- Parameter needed for extended error message generation for 'UPDATE' and +-- 'DELETE' (can be empty on INSERT) +-- 2. foreign_collection – name of collection of the triggered table that +-- will be used to SELECT +-- 3. foreign_column – column in the foreign table referencing +-- `own_table` DECLARE - table_name TEXT := TG_ARGV[0]; - column_name TEXT := TG_ARGV[1]; - foreign_key TEXT := TG_ARGV[2]; + -- Parameters from TRIGGER DEFINITION + -- Always required + own_collection TEXT := TG_ARGV[0]; + own_column TEXT := TG_ARGV[1]; + + -- Only for TG_OP in ('UPDATE', 'DELETE') + foreign_collection TEXT := TG_ARGV[2]; + foreign_column TEXT := TG_ARGV[3]; + + -- Calculated parameters + own_id INTEGER; foreign_id INTEGER; counted INTEGER; error_message TEXT; BEGIN IF (TG_OP = 'INSERT') THEN -- in case of INSERT the view is checked on itself so the own id is applicable - foreign_id := NEW.id; - ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN - foreign_id := hstore(OLD) -> foreign_key; - EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; + own_id := NEW.id; + ELSE + own_id := hstore(OLD) -> foreign_column; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', own_collection, own_id) INTO counted; IF (counted IS NULL) THEN -- if the earlier referenced row was deleted (in the same transaction) we can quit. RETURN NULL; END IF; END IF; - IF (foreign_id IS NOT NULL) THEN - EXECUTE format('SELECT %I FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; - IF (counted is NULL) THEN - error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); - IF TG_OP IN ('UPDATE', 'DELETE') THEN - error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); - END IF; - RAISE EXCEPTION '%', error_message; + EXECUTE format('SELECT %I FROM %I WHERE id = %L', own_column, own_collection, own_id) INTO counted; + IF (counted is NULL) THEN + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); + IF TG_OP IN ('UPDATE', 'DELETE') THEN + foreign_id := OLD.id; + error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); END IF; + RAISE EXCEPTION '%', error_message; END IF; RETURN NULL; -- AFTER TRIGGER needs no return END; $not_null_trigger$ language plpgsql; CREATE FUNCTION check_not_null_for_1_n() RETURNS trigger AS $not_null_trigger$ --- usage with 3 parameters IN TRIGGER DEFINITION: --- table_name: relation to check, usually a view --- column_name: field to check, usually a field in a view --- foreign_key: field name of triggered table, that will be used to SELECT --- the values to check the not null. Can be empty on INSERT as then unused. +-- Parameters required for all operation types +-- 0. own_table – name of the table on which the trigger is defined +-- 1. own_column – column in `own_table` referencing +-- `foreign_table` +-- 2. foreign_table – name of the triggered table, that will be used to SELECT +-- 3. foreign_column – column in the foreign table referencing +-- `own_table` DECLARE - table_name TEXT := TG_ARGV[0]; - column_name TEXT := TG_ARGV[1]; - foreign_key TEXT := TG_ARGV[2]; + -- Parameters from TRIGGER DEFINITION + -- Always required + own_table TEXT := TG_ARGV[0]; + own_column TEXT := TG_ARGV[1]; + foreign_table TEXT := TG_ARGV[2]; + foreign_column TEXT := TG_ARGV[3]; + + -- Calculated parameters + own_collection TEXT; + foreign_collection TEXT; + own_id INTEGER; foreign_id INTEGER; counted INTEGER; error_message TEXT; BEGIN IF (TG_OP = 'INSERT') THEN -- in case of INSERT the view is checked on itself so the own id is applicable - foreign_id := NEW.id; - ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN - foreign_id := hstore(OLD) -> foreign_key; - EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; + own_id := NEW.id; + ELSE + own_id := hstore(OLD) -> foreign_column; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', own_table, own_id) INTO counted; IF (counted IS NULL) THEN -- if the earlier referenced row was deleted (in the same transaction) we can quit. RETURN NULL; END IF; END IF; - IF (foreign_id IS NOT NULL) THEN - EXECUTE format('SELECT array_length(%I, 1) FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; - IF (counted is NULL) THEN - error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); - IF TG_OP IN ('UPDATE', 'DELETE') THEN - error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); - END IF; - RAISE EXCEPTION '%', error_message; + EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', foreign_table, foreign_column, own_id) INTO counted; + IF (counted is NULL) THEN + own_collection := SUBSTRING(own_table FOR LENGTH(own_table) - 2); + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); + IF TG_OP IN ('UPDATE', 'DELETE') THEN + foreign_collection := SUBSTRING(foreign_table FOR LENGTH(foreign_table) - 2); + foreign_id := OLD.id; + error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); END IF; + RAISE EXCEPTION '%', error_message; END IF; RETURN NULL; -- AFTER TRIGGER needs no return END; @@ -253,23 +277,24 @@ $not_null_trigger$ language plpgsql; CREATE FUNCTION check_not_null_for_n_m() RETURNS trigger AS $not_null_trigger$ -- Parameters required for both INSERT and DELETE operations -- 0. intermediate_table_name – name of the n:m table --- 1. own_collection – name of the table on which the trigger is defined --- 2. own_column – column in `own_collection` referencing +-- 1. own_table – name of the table on which the trigger is defined +-- 2. own_column – column in `own_table` referencing -- `foreign_collection` -- 3. intermediate_table_own_key – column in the n:m table referencing --- `own_collection` +-- `own_table` -- -- Parameters needed for extended error message generation for 'DELETE' -- (can be empty on INSERT) -- 4. intermediate_table_foreign_key – column in the n:m table referencing --- `foreign_collection` --- 5. foreign_collection – name of the foreign table +-- the foreign table +-- 5. foreign_collection – name of the collection of the foreign table -- 6. foreign_column – column in the foreign table referencing -- `own_collection` DECLARE + -- Parameters from TRIGGER DEFINITION -- Always required intermediate_table_name TEXT := TG_ARGV[0]; - own_collection TEXT := TG_ARGV[1]; + own_table TEXT := TG_ARGV[1]; own_column TEXT := TG_ARGV[2]; intermediate_table_own_key TEXT := TG_ARGV[3]; @@ -278,35 +303,40 @@ DECLARE foreign_collection TEXT := TG_ARGV[5]; foreign_column TEXT := TG_ARGV[6]; - -- Calculated + -- Calculated parameters + own_collection TEXT; own_id INTEGER; foreign_id INTEGER; counted INTEGER; error_message TEXT; BEGIN IF (TG_OP = 'INSERT') THEN + -- in case of INSERT the view is checked on itself so the own id is applicable own_id := NEW.id; ELSE own_id := hstore(OLD) -> intermediate_table_own_key; - foreign_id := hstore(OLD) -> intermediate_table_foreign_key; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', own_table, own_id) INTO counted; + IF (counted IS NULL) THEN + -- if the earlier referenced row was deleted (in the same transaction) we can quit. + RETURN NULL; + END IF; END IF; EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', intermediate_table_name, intermediate_table_own_key, own_id) INTO counted; IF (counted is NULL) THEN + own_collection := SUBSTRING(own_table FOR LENGTH(own_table) - 2); error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); IF (TG_OP = 'DELETE') THEN + foreign_id := hstore(OLD) -> intermediate_table_foreign_key; error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); END IF; RAISE EXCEPTION '%', error_message; END IF; - RETURN NULL; + RETURN NULL; -- AFTER TRIGGER needs no return END; $not_null_trigger$ language plpgsql; --- Type definitions - - -- Table definitions CREATE TABLE action_worker_t ( @@ -2034,7 +2064,7 @@ CREATE VIEW "user" AS SELECT *, INNER JOIN meeting_user_t mu ON u.id = mu.user_id INNER JOIN meeting_t m ON mu.meeting_id = m.id WHERE u.id = u.id - + UNION -- Select committee_ids from committee managers @@ -2527,164 +2557,164 @@ FOR EACH ROW EXECUTE FUNCTION generate_sequence('topic_t', 'sequential_number', -- definition trigger not null for assignment.list_of_speakers_id against list_of_speakers.content_object_id_assignment_id CREATE CONSTRAINT TRIGGER tr_i_assignment_list_of_speakers_id AFTER INSERT ON assignment_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id'); CREATE CONSTRAINT TRIGGER tr_ud_assignment_list_of_speakers_id AFTER UPDATE OF content_object_id_assignment_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'content_object_id_assignment_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('assignment', 'list_of_speakers_id', 'list_of_speakers', 'content_object_id_assignment_id'); -- definition trigger not null for motion.list_of_speakers_id against list_of_speakers.content_object_id_motion_id CREATE CONSTRAINT TRIGGER tr_i_motion_list_of_speakers_id AFTER INSERT ON motion_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id'); CREATE CONSTRAINT TRIGGER tr_ud_motion_list_of_speakers_id AFTER UPDATE OF content_object_id_motion_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id', 'content_object_id_motion_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion', 'list_of_speakers_id', 'list_of_speakers', 'content_object_id_motion_id'); -- definition trigger not null for motion_block.list_of_speakers_id against list_of_speakers.content_object_id_motion_block_id CREATE CONSTRAINT TRIGGER tr_i_motion_block_list_of_speakers_id AFTER INSERT ON motion_block_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id'); CREATE CONSTRAINT TRIGGER tr_ud_motion_block_list_of_speakers_id AFTER UPDATE OF content_object_id_motion_block_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', 'content_object_id_motion_block_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('motion_block', 'list_of_speakers_id', 'list_of_speakers', 'content_object_id_motion_block_id'); -- definition trigger not null for poll_candidate_list.option_id against option.content_object_id_poll_candidate_list_id CREATE CONSTRAINT TRIGGER tr_i_poll_candidate_list_option_id AFTER INSERT ON poll_candidate_list_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id'); CREATE CONSTRAINT TRIGGER tr_ud_poll_candidate_list_option_id AFTER UPDATE OF content_object_id_poll_candidate_list_id OR DELETE ON option_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', 'content_object_id_poll_candidate_list_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('poll_candidate_list', 'option_id', 'option', 'content_object_id_poll_candidate_list_id'); -- definition trigger not null for topic.agenda_item_id against agenda_item.content_object_id_topic_id CREATE CONSTRAINT TRIGGER tr_i_topic_agenda_item_id AFTER INSERT ON topic_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id'); CREATE CONSTRAINT TRIGGER tr_ud_topic_agenda_item_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON agenda_item_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', 'content_object_id_topic_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'agenda_item_id', 'agenda_item', 'content_object_id_topic_id'); -- definition trigger not null for topic.list_of_speakers_id against list_of_speakers.content_object_id_topic_id CREATE CONSTRAINT TRIGGER tr_i_topic_list_of_speakers_id AFTER INSERT ON topic_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id'); CREATE CONSTRAINT TRIGGER tr_ud_topic_list_of_speakers_id AFTER UPDATE OF content_object_id_topic_id OR DELETE ON list_of_speakers_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', 'content_object_id_topic_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('topic', 'list_of_speakers_id', 'list_of_speakers', 'content_object_id_topic_id'); -- Create triggers checking foreign_id not null for 1:n relationships --- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector_t.used_as_default_projector_for_agenda_item_list_in_meeting_id +-- definition trigger not null for meeting.default_projector_agenda_item_list_ids against projector.used_as_default_projector_for_agenda_item_list_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_agenda_item_list_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_agenda_item_list_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_agenda_item_list_ids', 'projector_t', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_agenda_item_list_ids AFTER UPDATE OF used_as_default_projector_for_agenda_item_list_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_agenda_item_list_ids', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_agenda_item_list_ids', 'projector_t', 'used_as_default_projector_for_agenda_item_list_in_meeting_id'); --- definition trigger not null for meeting.default_projector_topic_ids against projector_t.used_as_default_projector_for_topic_in_meeting_id +-- definition trigger not null for meeting.default_projector_topic_ids against projector.used_as_default_projector_for_topic_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_topic_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_topic_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_topic_ids', 'projector_t', 'used_as_default_projector_for_topic_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_topic_ids AFTER UPDATE OF used_as_default_projector_for_topic_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_topic_ids', 'used_as_default_projector_for_topic_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_topic_ids', 'projector_t', 'used_as_default_projector_for_topic_in_meeting_id'); --- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projector_t.used_as_default_projector_for_list_of_speakers_in_meeting_id +-- definition trigger not null for meeting.default_projector_list_of_speakers_ids against projector.used_as_default_projector_for_list_of_speakers_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_list_of_speakers_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_list_of_speakers_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_list_of_speakers_ids', 'projector_t', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_list_of_speakers_ids AFTER UPDATE OF used_as_default_projector_for_list_of_speakers_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_list_of_speakers_ids', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_list_of_speakers_ids', 'projector_t', 'used_as_default_projector_for_list_of_speakers_in_meeting_id'); --- definition trigger not null for meeting.default_projector_current_los_ids against projector_t.used_as_default_projector_for_current_los_in_meeting_id +-- definition trigger not null for meeting.default_projector_current_los_ids against projector.used_as_default_projector_for_current_los_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_current_los_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_current_los_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_current_los_ids', 'projector_t', 'used_as_default_projector_for_current_los_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_current_los_ids AFTER UPDATE OF used_as_default_projector_for_current_los_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_current_los_ids', 'used_as_default_projector_for_current_los_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_current_los_ids', 'projector_t', 'used_as_default_projector_for_current_los_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_ids against projector_t.used_as_default_projector_for_motion_in_meeting_id +-- definition trigger not null for meeting.default_projector_motion_ids against projector.used_as_default_projector_for_motion_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_ids', 'projector_t', 'used_as_default_projector_for_motion_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_ids AFTER UPDATE OF used_as_default_projector_for_motion_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_ids', 'used_as_default_projector_for_motion_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_ids', 'projector_t', 'used_as_default_projector_for_motion_in_meeting_id'); --- definition trigger not null for meeting.default_projector_amendment_ids against projector_t.used_as_default_projector_for_amendment_in_meeting_id +-- definition trigger not null for meeting.default_projector_amendment_ids against projector.used_as_default_projector_for_amendment_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_amendment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_amendment_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_amendment_ids', 'projector_t', 'used_as_default_projector_for_amendment_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_amendment_ids AFTER UPDATE OF used_as_default_projector_for_amendment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_amendment_ids', 'used_as_default_projector_for_amendment_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_amendment_ids', 'projector_t', 'used_as_default_projector_for_amendment_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_block_ids against projector_t.used_as_default_projector_for_motion_block_in_meeting_id +-- definition trigger not null for meeting.default_projector_motion_block_ids against projector.used_as_default_projector_for_motion_block_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_block_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_block_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_block_ids', 'projector_t', 'used_as_default_projector_for_motion_block_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_block_ids AFTER UPDATE OF used_as_default_projector_for_motion_block_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_block_ids', 'used_as_default_projector_for_motion_block_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_block_ids', 'projector_t', 'used_as_default_projector_for_motion_block_in_meeting_id'); --- definition trigger not null for meeting.default_projector_assignment_ids against projector_t.used_as_default_projector_for_assignment_in_meeting_id +-- definition trigger not null for meeting.default_projector_assignment_ids against projector.used_as_default_projector_for_assignment_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_assignment_ids', 'projector_t', 'used_as_default_projector_for_assignment_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_ids AFTER UPDATE OF used_as_default_projector_for_assignment_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_ids', 'used_as_default_projector_for_assignment_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_assignment_ids', 'projector_t', 'used_as_default_projector_for_assignment_in_meeting_id'); --- definition trigger not null for meeting.default_projector_mediafile_ids against projector_t.used_as_default_projector_for_mediafile_in_meeting_id +-- definition trigger not null for meeting.default_projector_mediafile_ids against projector.used_as_default_projector_for_mediafile_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_mediafile_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_mediafile_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_mediafile_ids', 'projector_t', 'used_as_default_projector_for_mediafile_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_mediafile_ids AFTER UPDATE OF used_as_default_projector_for_mediafile_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_mediafile_ids', 'used_as_default_projector_for_mediafile_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_mediafile_ids', 'projector_t', 'used_as_default_projector_for_mediafile_in_meeting_id'); --- definition trigger not null for meeting.default_projector_message_ids against projector_t.used_as_default_projector_for_message_in_meeting_id +-- definition trigger not null for meeting.default_projector_message_ids against projector.used_as_default_projector_for_message_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_message_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_message_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_message_ids', 'projector_t', 'used_as_default_projector_for_message_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_message_ids AFTER UPDATE OF used_as_default_projector_for_message_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_message_ids', 'used_as_default_projector_for_message_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_message_ids', 'projector_t', 'used_as_default_projector_for_message_in_meeting_id'); --- definition trigger not null for meeting.default_projector_countdown_ids against projector_t.used_as_default_projector_for_countdown_in_meeting_id +-- definition trigger not null for meeting.default_projector_countdown_ids against projector.used_as_default_projector_for_countdown_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_countdown_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_countdown_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_countdown_ids', 'projector_t', 'used_as_default_projector_for_countdown_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_countdown_ids AFTER UPDATE OF used_as_default_projector_for_countdown_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_countdown_ids', 'used_as_default_projector_for_countdown_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_countdown_ids', 'projector_t', 'used_as_default_projector_for_countdown_in_meeting_id'); --- definition trigger not null for meeting.default_projector_assignment_poll_ids against projector_t.used_as_default_projector_for_assignment_poll_in_meeting_id +-- definition trigger not null for meeting.default_projector_assignment_poll_ids against projector.used_as_default_projector_for_assignment_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_assignment_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_assignment_poll_ids', 'projector_t', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_assignment_poll_ids AFTER UPDATE OF used_as_default_projector_for_assignment_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_assignment_poll_ids', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_assignment_poll_ids', 'projector_t', 'used_as_default_projector_for_assignment_poll_in_meeting_id'); --- definition trigger not null for meeting.default_projector_motion_poll_ids against projector_t.used_as_default_projector_for_motion_poll_in_meeting_id +-- definition trigger not null for meeting.default_projector_motion_poll_ids against projector.used_as_default_projector_for_motion_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_motion_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_poll_ids', 'projector_t', 'used_as_default_projector_for_motion_poll_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_motion_poll_ids AFTER UPDATE OF used_as_default_projector_for_motion_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_motion_poll_ids', 'used_as_default_projector_for_motion_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_motion_poll_ids', 'projector_t', 'used_as_default_projector_for_motion_poll_in_meeting_id'); --- definition trigger not null for meeting.default_projector_poll_ids against projector_t.used_as_default_projector_for_poll_in_meeting_id +-- definition trigger not null for meeting.default_projector_poll_ids against projector.used_as_default_projector_for_poll_in_meeting_id CREATE CONSTRAINT TRIGGER tr_i_meeting_default_projector_poll_ids AFTER INSERT ON meeting_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_poll_ids', ''); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_poll_ids', 'projector_t', 'used_as_default_projector_for_poll_in_meeting_id'); CREATE CONSTRAINT TRIGGER tr_ud_meeting_default_projector_poll_ids AFTER UPDATE OF used_as_default_projector_for_poll_in_meeting_id OR DELETE ON projector_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_projector_poll_ids', 'used_as_default_projector_for_poll_in_meeting_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting_t', 'default_projector_poll_ids', 'projector_t', 'used_as_default_projector_for_poll_in_meeting_id'); @@ -2693,10 +2723,10 @@ FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('meeting', 'default_project -- definition trigger not null for meeting_user.group_ids against group.meeting_user_ids through nm_group_meeting_user_ids_meeting_user_t CREATE CONSTRAINT TRIGGER tr_i_meeting_user_group_ids AFTER INSERT ON meeting_user_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user', 'group_ids', 'meeting_user_id'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user_t', 'group_ids', 'meeting_user_id'); CREATE CONSTRAINT TRIGGER tr_d_meeting_user_group_ids AFTER DELETE ON nm_group_meeting_user_ids_meeting_user_t INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user', 'group_ids', 'meeting_user_id', 'group_id', 'group', 'meeting_user_ids'); +FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('nm_group_meeting_user_ids_meeting_user_t', 'meeting_user_t', 'group_ids', 'meeting_user_id', 'group_id', 'group', 'meeting_user_ids'); diff --git a/dev/src/generate_sql_schema.py b/dev/src/generate_sql_schema.py index f8509f55..096c7ec3 100644 --- a/dev/src/generate_sql_schema.py +++ b/dev/src/generate_sql_schema.py @@ -6,7 +6,7 @@ from enum import Enum from pathlib import Path from string import Formatter -from textwrap import dedent +from textwrap import dedent, indent from typing import Any, TypedDict, cast from sqlfluff import fix @@ -83,7 +83,7 @@ def generate_the_code( ]: """ Return values: - pre_code: Type definitions etc., which should all appear before first table definitions + pre_code: Type definitions, generated trigger definitions etc., which should all appear before first table definitions table_name_code: All table definitions view_name_code: All view definitions, after all views, because of view field definition by sql alter_table_final_code: Changes on tables defining relations after, which should appear after all table/views definition to be sequence independant @@ -136,6 +136,11 @@ def generate_the_code( im_table_code = "" errors: list[str] = [] + for type_ in ["1_1", "1_n", "n_m"]: + pre_code += Helper.NOT_NULL_TRIGGER_FUNCTION_TEMPLATE.substitute( + cls.get_not_null_trigger_params(type_) + ) + for table_name, fields in InternalHelper.MODELS.items(): if table_name in ["_migration_index", "_meta"]: continue @@ -220,6 +225,154 @@ def generate_the_code( errors, ) + @staticmethod + def get_not_null_trigger_params(type_: str) -> dict[str, str]: + if type_ == "1_1": + docstring = dedent( + """\ + -- Parameters required for all operation types + -- 0. own_collection – name of the view on which the trigger is defined + -- 1. own_column – column in `own_table` referencing + -- `foreign_table` + -- + -- Parameter needed for extended error message generation for 'UPDATE' and + -- 'DELETE' (can be empty on INSERT) + -- 2. foreign_collection – name of collection of the triggered table that + -- will be used to SELECT + -- 3. foreign_column – column in the foreign table referencing + -- `own_table`""" + ) + parameters_declaration = indent( + dedent( + """\ + -- Parameters from TRIGGER DEFINITION + -- Always required + own_collection TEXT := TG_ARGV[0]; + own_column TEXT := TG_ARGV[1]; + + -- Only for TG_OP in ('UPDATE', 'DELETE') + foreign_collection TEXT := TG_ARGV[2]; + foreign_column TEXT := TG_ARGV[3]; + + -- Calculated parameters + own_id INTEGER; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT;""" + ), + " ", + ) + select_expression = "EXECUTE format('SELECT %I FROM %I WHERE id = %L', own_column, own_collection, own_id) INTO counted;" + + elif type_ == "1_n": + docstring = dedent( + """\ + -- Parameters required for all operation types + -- 0. own_table – name of the table on which the trigger is defined + -- 1. own_column – column in `own_table` referencing + -- `foreign_table` + -- 2. foreign_table – name of the triggered table, that will be used to SELECT + -- 3. foreign_column – column in the foreign table referencing + -- `own_table`""" + ) + parameters_declaration = indent( + dedent( + """\ + -- Parameters from TRIGGER DEFINITION + -- Always required + own_table TEXT := TG_ARGV[0]; + own_column TEXT := TG_ARGV[1]; + foreign_table TEXT := TG_ARGV[2]; + foreign_column TEXT := TG_ARGV[3]; + + -- Calculated parameters + own_collection TEXT; + foreign_collection TEXT; + own_id INTEGER; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT;""" + ), + " ", + ) + select_expression = "EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', foreign_table, foreign_column, own_id) INTO counted;" + + else: + docstring = dedent( + """\ + -- Parameters required for both INSERT and DELETE operations + -- 0. intermediate_table_name – name of the n:m table + -- 1. own_table – name of the table on which the trigger is defined + -- 2. own_column – column in `own_table` referencing + -- `foreign_collection` + -- 3. intermediate_table_own_key – column in the n:m table referencing + -- `own_table` + -- + -- Parameters needed for extended error message generation for 'DELETE' + -- (can be empty on INSERT) + -- 4. intermediate_table_foreign_key – column in the n:m table referencing + -- the foreign table + -- 5. foreign_collection – name of the collection of the foreign table + -- 6. foreign_column – column in the foreign table referencing + -- `own_collection`""" + ) + parameters_declaration = indent( + dedent( + """\ + -- Parameters from TRIGGER DEFINITION + -- Always required + intermediate_table_name TEXT := TG_ARGV[0]; + own_table TEXT := TG_ARGV[1]; + own_column TEXT := TG_ARGV[2]; + intermediate_table_own_key TEXT := TG_ARGV[3]; + + -- Only for TG_OP = 'DELETE' + intermediate_table_foreign_key TEXT := TG_ARGV[4]; + foreign_collection TEXT := TG_ARGV[5]; + foreign_column TEXT := TG_ARGV[6]; + + -- Calculated parameters + own_collection TEXT; + own_id INTEGER; + foreign_id INTEGER; + counted INTEGER; + error_message TEXT;""" + ), + " ", + ) + select_expression = "EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', intermediate_table_name, intermediate_table_own_key, own_id) INTO counted;" + + return { + "trigger_type": type_, + "docstring": docstring, + "parameters_declaration": parameters_declaration, + "foreign_column": ( + "intermediate_table_own_key" if type_ == "n_m" else "foreign_column" + ), + "query_relation": "own_collection" if type_ == "1_1" else "own_table", + "select_expression": select_expression, + "own_collection_definition": ( + "" + if type_ == "1_1" + else f"\n {Helper.COLLECTION_FROM_TABLE_TEMPLATE.substitute({'parameter': 'own_collection', 'table_t': 'own_table'})}" + ), + "ud_operations_filter": ( + "(TG_OP = 'DELETE')" + if type_ == "n_m" + else "TG_OP IN ('UPDATE', 'DELETE')" + ), + "foreign_collection_definition": ( + f"\n {Helper.COLLECTION_FROM_TABLE_TEMPLATE.substitute({'parameter': 'foreign_collection', 'table_t': 'foreign_table'})}" + if type_ == "1_n" + else "" + ), + "foreign_id": ( + "hstore(OLD) -> intermediate_table_foreign_key" + if type_ == "n_m" + else "OLD.id" + ), + } + @classmethod def get_method( cls, fname: str, fdata: dict[str, Any] @@ -609,35 +762,43 @@ def get_trigger_generate_partitioned_sequence( @classmethod def get_trigger_check_not_null_for_1_1_relation( - cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str + cls, + own_collection: str, + own_column: str, + foreign_collection: str, + foreign_column: str, ) -> str: - own_table_t = HelperGetNames.get_table_name(own_table) - foreign_table_t = HelperGetNames.get_table_name(foreign_table) + own_table_t = HelperGetNames.get_table_name(own_collection) + foreign_table_t = HelperGetNames.get_table_name(foreign_collection) return dedent( f""" - -- definition trigger not null for {own_table}.{own_column} against {foreign_table}.{foreign_column} - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_table}', '{own_column}', ''); + -- definition trigger not null for {own_collection}.{own_column} against {foreign_collection}.{foreign_column} + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_insert_trigger_name(own_collection, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_collection}', '{own_column}'); - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_table}', '{own_column}', '{foreign_column}'); + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_1_1_rel_upd_del_trigger_name(own_collection, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_1('{own_collection}', '{own_column}', '{foreign_collection}', '{foreign_column}'); """ ) @classmethod def get_trigger_check_not_null_for_1_n( - cls, own_table: str, own_column: str, foreign_table: str, foreign_column: str + cls, + own_collection: str, + own_column: str, + foreign_collection: str, + foreign_column: str, ) -> str: - own_table_t = HelperGetNames.get_table_name(own_table) - foreign_table_t = HelperGetNames.get_table_name(foreign_table) + own_table_t = HelperGetNames.get_table_name(own_collection) + foreign_table_t = HelperGetNames.get_table_name(foreign_collection) return dedent( f""" - -- definition trigger not null for {own_table}.{own_column} against {foreign_table_t}.{foreign_column} - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_table, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table}', '{own_column}', ''); + -- definition trigger not null for {own_collection}.{own_column} against {foreign_collection}.{foreign_column} + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_insert_trigger_name(own_collection, own_column)} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table_t}', '{own_column}', '{foreign_table_t}', '{foreign_column}'); - CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_table, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table}', '{own_column}', '{foreign_column}'); + CREATE CONSTRAINT TRIGGER {HelperGetNames.get_not_null_rel_list_upd_del_trigger_name(own_collection, own_column)} AFTER UPDATE OF {foreign_column} OR DELETE ON {foreign_table_t} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_1_n('{own_table_t}', '{own_column}', '{foreign_table_t}', '{foreign_column}'); """ ) @@ -646,10 +807,10 @@ def get_trigger_check_not_null_for_1_n( def get_trigger_check_not_null_for_n_m( cls, own_table_field: TableFieldType, foreign_table_field: TableFieldType ) -> str: - own_table = own_table_field.table + own_collection = own_table_field.table own_column = own_table_field.column - own_table_t = HelperGetNames.get_table_name(own_table) - foreign_table = foreign_table_field.table + own_table = HelperGetNames.get_table_name(own_collection) + foreign_collection = foreign_table_field.table foreign_column = foreign_table_field.column intermediate_table_name = HelperGetNames.get_nm_table_name( own_table_field, foreign_table_field @@ -662,12 +823,12 @@ def get_trigger_check_not_null_for_n_m( ) return dedent( f""" - -- definition trigger not null for {own_table}.{own_column} against {foreign_table}.{foreign_column} through {intermediate_table_name} - CREATE CONSTRAINT TRIGGER tr_i_{own_table}_{own_column} AFTER INSERT ON {own_table_t} INITIALLY DEFERRED + -- definition trigger not null for {own_collection}.{own_column} against {foreign_collection}.{foreign_column} through {intermediate_table_name} + CREATE CONSTRAINT TRIGGER tr_i_{own_collection}_{own_column} AFTER INSERT ON {own_table} INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('{intermediate_table_name}', '{own_table}', '{own_column}', '{intermediate_table_own_key}'); - CREATE CONSTRAINT TRIGGER tr_d_{own_table}_{own_column} AFTER DELETE ON {intermediate_table_name} INITIALLY DEFERRED - FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('{intermediate_table_name}', '{own_table}', '{own_column}', '{intermediate_table_own_key}', '{intermediate_table_foreign_key}', '{foreign_table}', '{foreign_column}'); + CREATE CONSTRAINT TRIGGER tr_d_{own_collection}_{own_column} AFTER DELETE ON {intermediate_table_name} INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION check_not_null_for_n_m('{intermediate_table_name}', '{own_table}', '{own_column}', '{intermediate_table_own_key}', '{intermediate_table_foreign_key}', '{foreign_collection}', '{foreign_column}'); """ ) @@ -972,112 +1133,44 @@ class Helper: $read_only_trigger$ LANGUAGE plpgsql; """ ) - - for type_, field_check in { - "1_1": "%I", - "1_n": "array_length(%I, 1)", - }.items(): - FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( - f""" - CREATE FUNCTION check_not_null_for_{type_}() RETURNS trigger AS $not_null_trigger$ - -- usage with 3 parameters IN TRIGGER DEFINITION: - -- table_name: relation to check, usually a view - -- column_name: field to check, usually a field in a view - -- foreign_key: field name of triggered table, that will be used to SELECT - -- the values to check the not null. Can be empty on INSERT as then unused. - DECLARE - table_name TEXT := TG_ARGV[0]; - column_name TEXT := TG_ARGV[1]; - foreign_key TEXT := TG_ARGV[2]; - foreign_id INTEGER; - counted INTEGER; - error_message TEXT; - BEGIN - IF (TG_OP = 'INSERT') THEN - -- in case of INSERT the view is checked on itself so the own id is applicable - foreign_id := NEW.id; - ELSIF TG_OP IN ('UPDATE', 'DELETE') THEN - foreign_id := hstore(OLD) -> foreign_key; - EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', table_name, foreign_id) INTO counted; - IF (counted IS NULL) THEN - -- if the earlier referenced row was deleted (in the same transaction) we can quit. - RETURN NULL; + NOT_NULL_TRIGGER_FUNCTION_TEMPLATE = string.Template( + dedent( + """ + CREATE FUNCTION check_not_null_for_${trigger_type}() RETURNS trigger AS $$not_null_trigger$$ + ${docstring} + DECLARE + ${parameters_declaration} + BEGIN + IF (TG_OP = 'INSERT') THEN + -- in case of INSERT the view is checked on itself so the own id is applicable + own_id := NEW.id; + ELSE + own_id := hstore(OLD) -> ${foreign_column}; + EXECUTE format('SELECT 1 FROM %I WHERE "id" = %L', ${query_relation}, own_id) INTO counted; + IF (counted IS NULL) THEN + -- if the earlier referenced row was deleted (in the same transaction) we can quit. + RETURN NULL; + END IF; END IF; - END IF; - IF (foreign_id IS NOT NULL) THEN - EXECUTE format('SELECT {field_check} FROM %I WHERE id = %s', column_name, table_name, foreign_id) INTO counted; - IF (counted is NULL) THEN - error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, table_name, foreign_id, column_name); - IF TG_OP IN ('UPDATE', 'DELETE') THEN - error_message := error_message || format(' from relationship before %s/%s', OLD.id, foreign_key); + ${select_expression} + IF (counted is NULL) THEN${own_collection_definition} + error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); + IF ${ud_operations_filter} THEN${foreign_collection_definition} + foreign_id := ${foreign_id}; + error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); END IF; RAISE EXCEPTION '%', error_message; END IF; - END IF; - RETURN NULL; -- AFTER TRIGGER needs no return - END; - $not_null_trigger$ language plpgsql; + RETURN NULL; -- AFTER TRIGGER needs no return + END; + $$not_null_trigger$$ language plpgsql; """ ) - - FILE_TEMPLATE_CONSTANT_DEFINITIONS += dedent( - """ - CREATE FUNCTION check_not_null_for_n_m() RETURNS trigger AS $not_null_trigger$ - -- Parameters required for both INSERT and DELETE operations - -- 0. intermediate_table_name – name of the n:m table - -- 1. own_collection – name of the table on which the trigger is defined - -- 2. own_column – column in `own_collection` referencing - -- `foreign_collection` - -- 3. intermediate_table_own_key – column in the n:m table referencing - -- `own_collection` - -- - -- Parameters needed for extended error message generation for 'DELETE' - -- (can be empty on INSERT) - -- 4. intermediate_table_foreign_key – column in the n:m table referencing - -- `foreign_collection` - -- 5. foreign_collection – name of the foreign table - -- 6. foreign_column – column in the foreign table referencing - -- `own_collection` - DECLARE - -- Always required - intermediate_table_name TEXT := TG_ARGV[0]; - own_collection TEXT := TG_ARGV[1]; - own_column TEXT := TG_ARGV[2]; - intermediate_table_own_key TEXT := TG_ARGV[3]; - - -- Only for TG_OP = 'DELETE' - intermediate_table_foreign_key TEXT := TG_ARGV[4]; - foreign_collection TEXT := TG_ARGV[5]; - foreign_column TEXT := TG_ARGV[6]; - - -- Calculated - own_id INTEGER; - foreign_id INTEGER; - counted INTEGER; - error_message TEXT; - BEGIN - IF (TG_OP = 'INSERT') THEN - own_id := NEW.id; - ELSE - own_id := hstore(OLD) -> intermediate_table_own_key; - foreign_id := hstore(OLD) -> intermediate_table_foreign_key; - END IF; - - EXECUTE format('SELECT 1 FROM %I WHERE %I = %L', intermediate_table_name, intermediate_table_own_key, own_id) INTO counted; - IF (counted is NULL) THEN - error_message := format('Trigger %s: NOT NULL CONSTRAINT VIOLATED for %s/%s/%s', TG_NAME, own_collection, own_id, own_column); - IF (TG_OP = 'DELETE') THEN - error_message := error_message || format(' from relationship before %s/%s/%s', foreign_collection, foreign_id, foreign_column); - END IF; - RAISE EXCEPTION '%', error_message; - END IF; - RETURN NULL; - END; - $not_null_trigger$ language plpgsql; - """ ) - + COLLECTION_FROM_TABLE_TEMPLATE = string.Template( + "${parameter} := SUBSTRING(${table_t} FOR LENGTH(${table_t}) - 2);" + ) FIELD_TEMPLATE = string.Template( " ${field_name} ${type}${primary_key}${required}${unique}${check_enum}${minimum}${minLength}${default},\n" ) @@ -1657,7 +1750,6 @@ def main() -> None: dest.write("-- MODELS_YML_CHECKSUM = " + repr(checksum) + "\n") dest.write("\n\n-- Function and meta table definitions\n") dest.write(Helper.FILE_TEMPLATE_CONSTANT_DEFINITIONS) - dest.write("\n\n-- Type definitions\n") dest.write(pre_code) dest.write("\n\n-- Table definitions\n") dest.write(table_name_code) diff --git a/models.yml b/models.yml index 4918a7b8..5ce95d94 100644 --- a/models.yml +++ b/models.yml @@ -4487,8 +4487,8 @@ user: \ FROM (\n -- Select committee_ids from meetings the user is part of\n \ \ SELECT m.committee_id\n FROM user_t u\n INNER JOIN meeting_user_t\ \ mu ON u.id = mu.user_id\n INNER JOIN meeting_t m ON mu.meeting_id =\ - \ m.id\n WHERE u.id = u.id\n \n UNION\n\n -- Select committee_ids\ - \ from committee managers\n SELECT cmu.committee_id\n FROM nm_committee_manager_ids_user_t\ + \ m.id\n WHERE u.id = u.id\n\n UNION\n\n -- Select committee_ids from\ + \ committee managers\n SELECT cmu.committee_id\n FROM nm_committee_manager_ids_user_t\ \ cmu\n WHERE cmu.user_id = u.id\n\n UNION\n\n -- Select home_committee_id\ \ from user\n SELECT home_committee_id\n FROM user_t\n WHERE home_committee_id\ \ IS NOT NULL\n ) AS committee_id\n) AS committee_ids\n" From 3ee58d1d4c7906bcdb46922a4ffd55a94109dbb7 Mon Sep 17 00:00:00 2001 From: Viktoriia Krasnovyd <114735598+vkrasnovyd@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:55:15 +0100 Subject: [PATCH 134/142] [rel-DB] Isolate rows for user.committee_ids calculation (#381) --------- Co-authored-by: rrenkert Co-authored-by: Hannes Janott --- collections/user.yml | 17 ++++++++--------- dev/sql/schema_relational.sql | 19 +++++++++---------- models.yml | 16 ++++++++-------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/collections/user.yml b/collections/user.yml index cbed5da9..b1c372c7 100644 --- a/collections/user.yml +++ b/collections/user.yml @@ -100,14 +100,13 @@ committee_ids: description: "Calculated field: Returns committee_ids, where the user is manager or member in a meeting" sql: | ( - SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL) + SELECT array_agg(DISTINCT ci.committee_id ORDER BY ci.committee_id) FROM ( -- Select committee_ids from meetings the user is part of SELECT m.committee_id - FROM user_t u - INNER JOIN meeting_user_t mu ON u.id = mu.user_id - INNER JOIN meeting_t m ON mu.meeting_id = m.id - WHERE u.id = u.id + FROM meeting_user_t AS mu + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id UNION @@ -119,10 +118,10 @@ committee_ids: UNION -- Select home_committee_id from user - SELECT home_committee_id - FROM user_t - WHERE home_committee_id IS NOT NULL - ) AS committee_id + SELECT u_hc.home_committee_id + FROM user_t u_hc + WHERE u_hc.home_committee_id IS NOT NULL AND u_hc.id = u.id + ) AS ci ) AS committee_ids # committee specific permissions committee_management_ids: diff --git a/dev/sql/schema_relational.sql b/dev/sql/schema_relational.sql index 64a9d6ac..a6ae677f 100644 --- a/dev/sql/schema_relational.sql +++ b/dev/sql/schema_relational.sql @@ -1,7 +1,7 @@ -- schema_relational.sql for initial database setup OpenSlides -- Code generated. DO NOT EDIT. --- MODELS_YML_CHECKSUM = '70d875293a2d83c994af88245cd89ee5' +-- MODELS_YML_CHECKSUM = 'e0d5579236d105d49e687ac5999f0184' -- Function and meta table definitions @@ -2056,14 +2056,13 @@ FROM topic_t t; CREATE VIEW "user" AS SELECT *, (select array_agg(n.meeting_id ORDER BY n.meeting_id) from nm_meeting_present_user_ids_user_t n where n.user_id = u.id) as is_present_in_meeting_ids, ( - SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL) + SELECT array_agg(DISTINCT ci.committee_id ORDER BY ci.committee_id) FROM ( -- Select committee_ids from meetings the user is part of SELECT m.committee_id - FROM user_t u - INNER JOIN meeting_user_t mu ON u.id = mu.user_id - INNER JOIN meeting_t m ON mu.meeting_id = m.id - WHERE u.id = u.id + FROM meeting_user_t AS mu + INNER JOIN meeting_t AS m ON m.id = mu.meeting_id + WHERE mu.user_id = u.id UNION @@ -2075,10 +2074,10 @@ CREATE VIEW "user" AS SELECT *, UNION -- Select home_committee_id from user - SELECT home_committee_id - FROM user_t - WHERE home_committee_id IS NOT NULL - ) AS committee_id + SELECT u_hc.home_committee_id + FROM user_t u_hc + WHERE u_hc.home_committee_id IS NOT NULL AND u_hc.id = u.id + ) AS ci ) AS committee_ids , (select array_agg(n.committee_id ORDER BY n.committee_id) from nm_committee_manager_ids_user_t n where n.user_id = u.id) as committee_management_ids, diff --git a/models.yml b/models.yml index 5ce95d94..23cbefd1 100644 --- a/models.yml +++ b/models.yml @@ -4483,15 +4483,15 @@ user: read_only: true description: 'Calculated field: Returns committee_ids, where the user is manager or member in a meeting' - sql: "(\n SELECT array_remove(array_agg(DISTINCT committee_id ORDER BY committee_id),NULL)\n\ + sql: "(\n SELECT array_agg(DISTINCT ci.committee_id ORDER BY ci.committee_id)\n\ \ FROM (\n -- Select committee_ids from meetings the user is part of\n \ - \ SELECT m.committee_id\n FROM user_t u\n INNER JOIN meeting_user_t\ - \ mu ON u.id = mu.user_id\n INNER JOIN meeting_t m ON mu.meeting_id =\ - \ m.id\n WHERE u.id = u.id\n\n UNION\n\n -- Select committee_ids from\ - \ committee managers\n SELECT cmu.committee_id\n FROM nm_committee_manager_ids_user_t\ - \ cmu\n WHERE cmu.user_id = u.id\n\n UNION\n\n -- Select home_committee_id\ - \ from user\n SELECT home_committee_id\n FROM user_t\n WHERE home_committee_id\ - \ IS NOT NULL\n ) AS committee_id\n) AS committee_ids\n" + \ SELECT m.committee_id\n FROM meeting_user_t AS mu\n INNER JOIN meeting_t\ + \ AS m ON m.id = mu.meeting_id\n WHERE mu.user_id = u.id\n\n UNION\n\n\ + \ -- Select committee_ids from committee managers\n SELECT cmu.committee_id\n\ + \ FROM nm_committee_manager_ids_user_t cmu\n WHERE cmu.user_id = u.id\n\ + \n UNION\n\n -- Select home_committee_id from user\n SELECT u_hc.home_committee_id\n\ + \ FROM user_t u_hc\n WHERE u_hc.home_committee_id IS NOT NULL AND u_hc.id\ + \ = u.id\n ) AS ci\n) AS committee_ids\n" committee_management_ids: type: relation-list to: committee/manager_ids From 7ad7d26bfebc28b1d1883474efecf4e4e933de16 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 14:48:47 +0100 Subject: [PATCH 135/142] Moving makefile and adjusting references to its maketargets --- .github/workflows/continuous_integration.yml | 12 ++++++------ dev/Makefile => Makefile | 2 +- dev/setup.cfg => setup.cfg | 0 3 files changed, 7 insertions(+), 7 deletions(-) rename dev/Makefile => Makefile (98%) rename dev/setup.cfg => setup.cfg (100%) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 64537d94..1247585a 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -31,27 +31,27 @@ jobs: - name: Check black if: always() - run: make check-black + run: make -C .. check-black - name: Check isort if: always() - run: make check-isort + run: make -C .. check-isort - name: Check flake8 if: always() - run: make flake8 + run: make -C .. flake8 - name: Check mypy if: always() - run: make mypy + run: make -C .. mypy - name: Check pyupgrade if: always() - run: make pyupgrade + run: make -C .. pyupgrade - name: Validate models.yml if: always() - run: make validate-models + run: make -C .. validate-models - name: Generate models.yml from collections run: python src/join_models_yml.py diff --git a/dev/Makefile b/Makefile similarity index 98% rename from dev/Makefile rename to Makefile index e8ea715d..484e98fc 100644 --- a/dev/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Commands inside the container -paths = src/ tests/ +paths = dev/src/ dev/tests/ all: pyupgrade black autoflake isort flake8 mypy sqlfluff diff --git a/dev/setup.cfg b/setup.cfg similarity index 100% rename from dev/setup.cfg rename to setup.cfg From ee16c02c3012eaf239bdf0d63f57bc34baabba52 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 14:54:27 +0100 Subject: [PATCH 136/142] Change Workflow CI environment to root --- .github/workflows/continuous_integration.yml | 21 +++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 1247585a..ea893275 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -14,9 +14,6 @@ jobs: validate-models: name: CI runs-on: ubuntu-latest - defaults: - run: - working-directory: dev/ steps: - uses: actions/checkout@v6 @@ -27,38 +24,38 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install requirements - run: pip install -r requirements.txt + run: pip install -r dev/requirements.txt - name: Check black if: always() - run: make -C .. check-black + run: make check-black - name: Check isort if: always() - run: make -C .. check-isort + run: make check-isort - name: Check flake8 if: always() - run: make -C .. flake8 + run: make flake8 - name: Check mypy if: always() - run: make -C .. mypy + run: make mypy - name: Check pyupgrade if: always() - run: make -C .. pyupgrade + run: make pyupgrade - name: Validate models.yml if: always() - run: make -C .. validate-models + run: make validate-models - name: Generate models.yml from collections - run: python src/join_models_yml.py + run: python dev/src/join_models_yml.py - name: Check if models.yml is up-to-date run: | - if git diff --exit-code ../models.yml; then + if git diff --exit-code ./models.yml; then echo "✅ models.yml is up-to-date" else echo "❌ models.yml is out of sync with collections/" From 0a022d097c42a04ebf5e98dc2a8089d29ecbe129 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 14:56:53 +0100 Subject: [PATCH 137/142] Adjusted hard coded paths in Makefile --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 484e98fc..ba0e7429 100644 --- a/Makefile +++ b/Makefile @@ -28,16 +28,16 @@ flake8: flake8 $(paths) mypy: - mypy src/ + mypy dev/src/ sqlfluff: sqlfluff fix --dialect postgres --verbose validate-models: - python -m src.validate + python -m dev/src.validate generate-relational-schema: - python -m src.generate_sql_schema + python -m dev/src.generate_sql_schema drop-database: dropdb -f -e --if-exists -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} From c2f5a0df635819f803a03b96d645b173b20706c0 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 15:03:45 +0100 Subject: [PATCH 138/142] Adjusted paths again. Moved .sqlfuffignore --- dev/.sqlfluffignore => .sqlfluffignore | 0 Makefile | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename dev/.sqlfluffignore => .sqlfluffignore (100%) diff --git a/dev/.sqlfluffignore b/.sqlfluffignore similarity index 100% rename from dev/.sqlfluffignore rename to .sqlfluffignore diff --git a/Makefile b/Makefile index ba0e7429..4a7135d5 100644 --- a/Makefile +++ b/Makefile @@ -34,10 +34,10 @@ sqlfluff: sqlfluff fix --dialect postgres --verbose validate-models: - python -m dev/src.validate + python -m dev.src.validate generate-relational-schema: - python -m dev/src.generate_sql_schema + python -m dev.src.generate_sql_schema drop-database: dropdb -f -e --if-exists -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} From 46400c0246878b79d91b3b31ae9e78075fa9924a Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 15:28:29 +0100 Subject: [PATCH 139/142] Calling isort on base.py and test_generic_relations.py --- dev/tests/base.py | 1 - dev/tests/test_generic_relations.py | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/tests/base.py b/dev/tests/base.py index c108ab80..fd251827 100644 --- a/dev/tests/base.py +++ b/dev/tests/base.py @@ -5,7 +5,6 @@ import psycopg from psycopg import sql from psycopg.types.json import Jsonb - from src.db_utils import DbUtils from src.python_sql import Table diff --git a/dev/tests/test_generic_relations.py b/dev/tests/test_generic_relations.py index 099986a3..92de6c22 100644 --- a/dev/tests/test_generic_relations.py +++ b/dev/tests/test_generic_relations.py @@ -3,7 +3,6 @@ import psycopg import pytest from psycopg import sql - from src.db_utils import DbUtils from src.python_sql import Table from tests.base import BaseTestCase From 16bfcf49519d682254caae8a11360d93a2d434a3 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 16:19:35 +0100 Subject: [PATCH 140/142] Add dev to appy-test-data target --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4a7135d5..4f192632 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,11 @@ create-database: createdb -e -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME} apply-db-schema: - scripts/apply_db_schema.sh + dev/scripts/apply_db_schema.sh apply-test-data: - scripts/apply_data.sh base_data.sql - scripts/apply_data.sh test_data.sql + dev/scripts/apply_data.sh base_data.sql + dev/scripts/apply_data.sh test_data.sql create-database-with-schema: drop-database create-database apply-db-schema From 18558cc458cf2b74db1edce0a5d1495709399a2a Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 12 Feb 2026 17:12:11 +0100 Subject: [PATCH 141/142] Adjusting run-dev --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4f192632..9b1839db 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,8 @@ run-psql: # Docker manage commands run-dev: - USER_ID=$$(id -u $${USER}) GROUP_ID=$$(id -g $${USER}) docker compose up -d --build - docker compose exec models bash --rcfile /etc/bash_completion + USER_ID=$$(id -u $${USER}) GROUP_ID=$$(id -g $${USER}) docker compose -f dev/docker-compose.yml up -d --build + docker compose -f dev/docker-compose.yml exec models bash --rcfile /etc/bash_completion stop-dev: docker compose down From 9e0be22c236a730052cf900691a8bf8cef19b9e6 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Fri, 13 Feb 2026 15:34:53 +0100 Subject: [PATCH 142/142] Add -f flag to dev-stop --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9b1839db..f4ade3b1 100644 --- a/Makefile +++ b/Makefile @@ -66,4 +66,4 @@ run-dev: docker compose -f dev/docker-compose.yml exec models bash --rcfile /etc/bash_completion stop-dev: - docker compose down + docker compose -f dev/docker-compose.yml down