From 7e253f4d792524a3992a0deb94ab1fa1cadbe6c8 Mon Sep 17 00:00:00 2001 From: Joshua Sangmeister Date: Thu, 18 Apr 2024 11:53:28 +0200 Subject: [PATCH] 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