mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
70.9k stars 6.37k forks source link

too much recursion #4008

Open pashagolub opened 1 year ago

pashagolub commented 1 year ago

Description

When I'm trying to render my ER diagram in live editor or in github preview I have an error "too much recursion"

Steps to reproduce

  1. Open Live editor
  2. Paste my code
  3. Error occurs

Screenshots

No response

Code Sample

erDiagram
        rental{
                ~timestamp with time zone~ rental_date "NOT NULL"
                ~integer~ inventory_id "NOT NULL"
                ~integer~ customer_id "NOT NULL"
                ~timestamp with time zone~ return_date ""
                ~integer~ staff_id "NOT NULL"
                ~integer~ rental_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        film_actor{
                ~integer~ actor_id "NOT NULL"
                ~integer~ film_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        film{
                ~text~ title "NOT NULL"
                ~text~ description ""
                ~public.year~ release_year ""
                ~integer~ language_id "NOT NULL"
                ~integer~ original_language_id ""
                ~smallint~ length ""
                ~text[]~ special_features ""
                ~tsvector~ fulltext "NOT NULL"
                ~integer~ film_id "NOT NULL"
                ~smallint~ rental_duration "NOT NULL"
                ~numeric(4,2)~ rental_rate "NOT NULL"
                ~numeric(5,2)~ replacement_cost "NOT NULL"
                ~public.mpaa_rating~ rating ""
                ~timestamp with time zone~ last_update "NOT NULL"
}
        customer{
                ~integer~ store_id "NOT NULL"
                ~text~ first_name "NOT NULL"
                ~text~ last_name "NOT NULL"
                ~text~ email ""
                ~integer~ address_id "NOT NULL"
                ~integer~ active ""
                ~integer~ customer_id "NOT NULL"
                ~boolean~ activebool "NOT NULL"
                ~date~ create_date "NOT NULL"
                ~timestamp with time zone~ last_update ""
}
        film_category{
                ~integer~ film_id "NOT NULL"
                ~integer~ category_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        actor{
                ~text~ first_name "NOT NULL"
                ~text~ last_name "NOT NULL"
                ~integer~ actor_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        store{
                ~integer~ manager_staff_id "NOT NULL"
                ~integer~ address_id "NOT NULL"
                ~integer~ store_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        city{
                ~text~ city "NOT NULL"
                ~integer~ country_id "NOT NULL"
                ~integer~ city_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        language{
                ~character(20)~ name "NOT NULL"
                ~integer~ language_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        payment{
                ~integer~ customer_id "NOT NULL"
                ~integer~ staff_id "NOT NULL"
                ~integer~ rental_id "NOT NULL"
                ~numeric(5,2)~ amount "NOT NULL"
                ~timestamp with time zone~ payment_date "NOT NULL"
                ~integer~ payment_id "NOT NULL"
}
        category{
                ~text~ name "NOT NULL"
                ~integer~ category_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        inventory{
                ~integer~ film_id "NOT NULL"
                ~integer~ store_id "NOT NULL"
                ~integer~ inventory_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        address{
                ~text~ address "NOT NULL"
                ~text~ address2 ""
                ~text~ district "NOT NULL"
                ~integer~ city_id "NOT NULL"
                ~text~ postal_code ""
                ~text~ phone "NOT NULL"
                ~integer~ address_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        staff{
                ~text~ first_name "NOT NULL"
                ~text~ last_name "NOT NULL"
                ~integer~ address_id "NOT NULL"
                ~text~ email ""
                ~integer~ store_id "NOT NULL"
                ~text~ username "NOT NULL"
                ~text~ password ""
                ~bytea~ picture ""
                ~integer~ staff_id "NOT NULL"
                ~boolean~ active "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
        country{
                ~text~ country "NOT NULL"
                ~integer~ country_id "NOT NULL"
                ~timestamp with time zone~ last_update "NOT NULL"
}
film_actor }|..|| film : film_actor_film_id_fkey
film_actor }|..|| actor : film_actor_actor_id_fkey
address }|..|| city : address_city_id_fkey
city }|..|| country : city_country_id_fkey
customer }|..|| store : customer_store_id_fkey
customer }|..|| address : customer_address_id_fkey
film }|..|| language : film_original_language_id_fkey
film }|..|| language : film_language_id_fkey
film_category }|..|| film : film_category_film_id_fkey
film_category }|..|| category : film_category_category_id_fkey
inventory }|..|| store : inventory_store_id_fkey

Setup

All platforms are affected

Additional Context

No response

pashagolub commented 1 year ago

But if I change my comments it works:

erDiagram
        rental{
                ~timestamp with time zone~ rental_date "NN"
                ~integer~ inventory_id "NN"
                ~integer~ customer_id "NN"
                ~timestamp with time zone~ return_date
                ~integer~ staff_id "NN"
                ~integer~ rental_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        film_actor{
                ~integer~ actor_id "NN"
                ~integer~ film_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        film{
                ~text~ title "NN"
                ~text~ description
                ~public.year~ release_year
                ~integer~ language_id "NN"
                ~integer~ original_language_id
                ~smallint~ length
                ~text[]~ special_features
                ~tsvector~ fulltext "NN"
                ~integer~ film_id "NN"
                ~smallint~ rental_duration "NN"
                ~numeric(4,2)~ rental_rate "NN"
                ~numeric(5,2)~ replacement_cost "NN"
                ~public.mpaa_rating~ rating
                ~timestamp with time zone~ last_update "NN"
}
        customer{
                ~integer~ store_id "NN"
                ~text~ first_name "NN"
                ~text~ last_name "NN"
                ~text~ email
                ~integer~ address_id "NN"
                ~integer~ active
                ~integer~ customer_id "NN"
                ~boolean~ activebool "NN"
                ~date~ create_date "NN"
                ~timestamp with time zone~ last_update
}
        film_category{
                ~integer~ film_id "NN"
                ~integer~ category_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        actor{
                ~text~ first_name "NN"
                ~text~ last_name "NN"
                ~integer~ actor_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        store{
                ~integer~ manager_staff_id "NN"
                ~integer~ address_id "NN"
                ~integer~ store_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        city{
                ~text~ city "NN"
                ~integer~ country_id "NN"
                ~integer~ city_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        language{
                ~character(20)~ name "NN"
                ~integer~ language_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        payment{
                ~integer~ customer_id "NN"
                ~integer~ staff_id "NN"
                ~integer~ rental_id "NN"
                ~numeric(5,2)~ amount "NN"
                ~timestamp with time zone~ payment_date "NN"
                ~integer~ payment_id "NN"
}
        category{
                ~text~ name "NN"
                ~integer~ category_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        inventory{
                ~integer~ film_id "NN"
                ~integer~ store_id "NN"
                ~integer~ inventory_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        address{
                ~text~ address "NN"
                ~text~ address2
                ~text~ district "NN"
                ~integer~ city_id "NN"
                ~text~ postal_code
                ~text~ phone "NN"
                ~integer~ address_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
        staff{
                ~text~ first_name "NN"
                ~text~ last_name "NN"
                ~integer~ address_id "NN"
                ~text~ email
                ~integer~ store_id "NN"
                ~text~ username "NN"
                ~text~ password
                ~bytea~ picture
                ~integer~ staff_id "NN"
                ~boolean~ active "NN"
                ~timestamp with time zone~ last_update "NN"
}
        country{
                ~text~ country "NN"
                ~integer~ country_id "NN"
                ~timestamp with time zone~ last_update "NN"
}
film_actor }|..|| film : film_actor_film_id_fkey
film_actor }|..|| actor : film_actor_actor_id_fkey
address }|..|| city : address_city_id_fkey
city }|..|| country : city_country_id_fkey
customer }|..|| store : customer_store_id_fkey
customer }|..|| address : customer_address_id_fkey
film }|..|| language : film_original_language_id_fkey
film }|..|| language : film_language_id_fkey
film_category }|..|| film : film_category_film_id_fkey
film_category }|..|| category : film_category_category_id_fkey
inventory }|..|| store : inventory_store_id_fkey
ziegenberg commented 1 year ago

I tried to render the current schema of Opencast (Open Source Lecture Capture & Video Management for Education) and I also got "InternalError: too much recursion".

The code sample

``` erDiagram oc_workflow_operation{ ~text~ description ~text~ if_condition ~bigint~ id ~boolean~ abortable ~boolean~ continuable ~timestamp without time zone~ completed ~timestamp without time zone~ started ~character varying(255)~ exception_handler_workflow ~character varying(255)~ execution_host ~boolean~ fail_on_error ~integer~ failed_attempts ~bigint~ job ~integer~ max_attempts ~integer~ retry_strategy ~integer~ state ~character varying(255)~ template ~bigint~ time_in_queue ~bigint~ workflow_id ~integer~ position } oc_series{ ~character varying(128)~ organization ~character varying(128)~ id ~text~ access_control ~timestamp without time zone~ deletion_date ~text~ dublin_core ~timestamp without time zone~ modified_date } oc_host_registration{ ~bigint~ id ~boolean~ active ~character varying(255)~ host ~integer~ cores ~character varying(39)~ address ~boolean~ maintenance ~double precision~ max_load ~bigint~ memory ~character varying(255)~ node_name ~boolean~ online } oc_user_role{ ~bigint~ user_id ~bigint~ role_id } oc_capture_agent_role{ ~character varying(128)~ id ~character varying(128)~ organization ~character varying(255)~ role } oc_scheduled_extended_event{ ~character varying(128)~ organization ~character varying(128)~ mediapackage_id ~character varying(128)~ capture_agent_id ~text~ capture_agent_properties ~character varying(64)~ checksum ~timestamp without time zone~ end_date ~timestamp without time zone~ last_modified_date ~text~ presenters ~bigint~ recording_last_heard ~character varying(255)~ recording_state ~character varying(255)~ source ~timestamp without time zone~ start_date ~text~ workflow_properties } oc_user_ref{ ~bigint~ id ~character varying(255)~ email ~timestamp without time zone~ last_login ~character varying(255)~ login_mechanism ~character varying(255)~ name ~character varying(128)~ username ~character varying(128)~ organization } oc_user_ref_role{ ~bigint~ user_id ~bigint~ role_id } sequence{ ~character varying(50)~ seq_name ~numeric(38,0)~ seq_count } oc_assets_version_claim{ ~character varying(128)~ mediapackage_id ~bigint~ last_claimed } oc_series_elements{ ~character varying(128)~ series ~character varying(128)~ organization ~bytea~ data ~character varying(128)~ type } oc_job_argument{ ~bigint~ id ~text~ argument ~integer~ argument_index } oc_event_comment_reply{ ~bigint~ id ~character varying(255)~ author ~timestamp without time zone~ creation_date ~timestamp without time zone~ modification_date ~text~ text ~bigint~ event_comment_id } oc_assets_properties{ ~bigint~ id ~boolean~ val_bool ~timestamp without time zone~ val_date ~bigint~ val_long ~character varying(128)~ mediapackage_id ~character varying(128)~ namespace ~character varying(128)~ property_name ~character varying(255)~ val_string } oc_aws_asset_mapping{ ~bigint~ id ~timestamp without time zone~ deletion_date ~character varying(128)~ mediapackage_element ~character varying(128)~ mediapackage ~character varying(1024)~ object_key ~character varying(1024)~ object_version ~character varying(128)~ organization ~bigint~ version } oc_organization_node{ ~character varying(128)~ organization ~integer~ port ~character varying(255)~ name } oc_role{ ~bigint~ id ~character varying(255)~ description ~character varying(128)~ name ~character varying(128)~ organization } oc_tiss_schedule_event{ ~bigint~ id ~character varying(6)~ coursenumber ~timestamp without time zone~ created ~bigint~ job_id ~boolean~ notification ~character varying(128)~ organization ~character varying(255)~ payload ~timestamp without time zone~ processed ~character varying(5)~ semestercode ~character varying(36)~ series_id ~character varying(255)~ state } oc_organization_property{ ~character varying(128)~ organization ~text~ value ~character varying(255)~ name } oc_group{ ~bigint~ id ~character varying(255)~ description ~character varying(128)~ group_id ~character varying(128)~ name ~character varying(255)~ role ~character varying(128)~ organization } oc_event_comment{ ~bigint~ id ~character varying(255)~ author ~timestamp without time zone~ creation_date ~character varying(128)~ event ~timestamp without time zone~ modification_date ~character varying(128)~ organization ~character varying(255)~ reason ~boolean~ resolved_status ~text~ text } oc_tiss_series_context{ ~character varying(5)~ semestercode ~character varying(6)~ coursenumber ~character varying(128)~ organization ~character varying(36)~ series_id } oc_user_action{ ~bigint~ id ~timestamp without time zone~ created ~integer~ inpoint ~boolean~ playing ~integer~ length ~character varying(128)~ mediapackage ~integer~ outpoint ~text~ type ~character varying(50)~ session_id } oc_workflow_operation_configuration{ ~bigint~ workflow_operation_id ~text~ configuration_value ~character varying(255)~ configuration_key } oc_series_property{ ~character varying(128)~ series ~character varying(128)~ organization ~text~ value ~character varying(255)~ name } oc_search{ ~character varying(128)~ id ~text~ access_control ~timestamp without time zone~ deletion_date ~text~ mediapackage_xml ~timestamp without time zone~ modification_date ~character varying(128)~ series_id ~character varying(128)~ organization } oc_incident_text{ ~character varying(128)~ id ~character varying(2038)~ text } oc_annotation{ ~bigint~ id ~timestamp without time zone~ created ~integer~ inpoint ~integer~ length ~character varying(128)~ mediapackage ~integer~ outpoint ~boolean~ private ~character varying(128)~ session ~character varying(128)~ type ~character varying(255)~ user_id ~text~ value } oc_capture_agent_state{ ~character varying(128)~ organization ~character varying(128)~ id ~text~ configuration ~bigint~ last_heard_from ~text~ state ~text~ url } oc_job{ ~bigint~ id ~text~ creator ~timestamp without time zone~ date_completed ~timestamp without time zone~ date_created ~timestamp without time zone~ date_started ~boolean~ dispatchable ~double precision~ job_load ~character varying(128)~ operation ~character varying(128)~ organization ~text~ payload ~bigint~ queue_time ~bigint~ run_time ~integer~ status ~bigint~ instance_version ~bigint~ creator_service ~bigint~ processor_service ~bigint~ parent ~bigint~ root } oc_bundleinfo{ ~bigint~ id ~character varying(128)~ build_number ~bigint~ bundle_id ~character varying(128)~ bundle_name ~character varying(128)~ bundle_version ~character varying(128)~ db_schema_version ~character varying(128)~ host } oc_group_role{ ~bigint~ group_id ~bigint~ role_id } oc_workflow_configuration{ ~bigint~ workflow_id ~text~ configuration_value ~character varying(255)~ configuration_key } oc_scheduled_last_modified{ ~character varying(255)~ capture_agent_id ~timestamp without time zone~ last_modified } oc_adopter_registration{ ~character varying(64)~ adopter_key ~boolean~ agreed_to_policy ~boolean~ allows_error_reports ~boolean~ allows_statistics ~character varying(255)~ city ~boolean~ contact_me ~character varying(255)~ country ~timestamp without time zone~ created ~timestamp without time zone~ last_modified ~boolean~ delete_me ~character varying(255)~ department ~character varying(255)~ email ~character varying(255)~ first_name ~character varying(255)~ last_name ~character varying(255)~ organisation ~character varying(255)~ postal_code ~boolean~ registered ~character varying(255)~ statistic_key ~character varying(255)~ street ~character varying(255)~ street_no ~integer~ terms_version_agreed } oc_user{ ~bigint~ id ~character varying(256)~ email ~boolean~ manageable ~character varying(256)~ name ~text~ password ~character varying(128)~ username ~character varying(128)~ organization } oc_workflow{ ~text~ description ~bigint~ id ~character varying(255)~ creator_id ~timestamp without time zone~ date_completed ~timestamp without time zone~ date_created ~text~ mediapackage ~character varying(128)~ mediapackage_id ~character varying(255)~ organization_id ~character varying(128)~ series_id ~integer~ state ~character varying(255)~ template ~character varying(255)~ title ~character varying(255)~ mediapackageid ~character varying(255)~ organizationid } oc_user_session{ ~character varying(50)~ session_id ~character varying(255)~ user_agent ~character varying(255)~ user_id ~character varying(255)~ user_ip } oc_acl_managed_acl{ ~bigint~ pk ~text~ acl ~character varying(128)~ name ~character varying(128)~ organization_id } oc_group_member{ ~bigint~ group_id ~character varying(255)~ member } oc_tiss_event_context{ ~character varying(5)~ semestercode ~character varying(6)~ coursenumber ~bigint~ id ~timestamp without time zone~ begin ~character varying(36)~ event_id ~character varying(128)~ organization } oc_organization{ ~character varying(128)~ id ~character varying(255)~ admin_role ~character varying(255)~ anonymous_role ~character varying(255)~ name } oc_incident{ ~bigint~ id ~character varying(255)~ code ~text~ details ~bigint~ jobid ~text~ parameters ~integer~ severity ~timestamp without time zone~ timestamp } oc_assets_snapshot{ ~bigint~ id ~timestamp without time zone~ archival_date ~character varying(32)~ availability ~character varying(128)~ mediapackage_id ~text~ mediapackage_xml ~character varying(128)~ organization_id ~character varying(256)~ owner ~character varying(128)~ series_id ~character varying(256)~ storage_id ~bigint~ version } oc_service_registration{ ~bigint~ id ~boolean~ active ~integer~ error_state_trigger ~boolean~ job_producer ~boolean~ online ~timestamp without time zone~ online_from ~character varying(255)~ path ~integer~ service_state ~character varying(255)~ service_type ~timestamp without time zone~ state_changed ~integer~ warning_state_trigger ~bigint~ host_registration } oc_assets_asset{ ~bigint~ id ~character varying(64)~ checksum ~character varying(128)~ mediapackage_element_id ~character varying(255)~ mime_type ~bigint~ size ~character varying(256)~ storage_id ~bigint~ snapshot_id } oc_capture_agent_role }|..|| oc_capture_agent_state : fk_oc_capture_agent_role_organization oc_search }|..|| oc_organization : fk_oc_search_organization oc_series_property }|..|| oc_series : fk_oc_series_property_organization oc_group_member }|..|| oc_group : fk_oc_group_member_group_id oc_job_argument }|..|| oc_job : fk_oc_job_argument_id oc_group_role }|..|| oc_group : fk_oc_group_role_group_id oc_group_role }|..|| oc_role : fk_oc_group_role_role_id oc_group }|..|| oc_organization : fk_oc_group_organization oc_job }|..|| oc_service_registration : fk_oc_job_processor_service oc_job }|..|| oc_job : fk_oc_job_parent oc_job }|..|| oc_service_registration : fk_oc_job_creator_service oc_job }|..|| oc_job : fk_oc_job_root oc_user }|..|| oc_organization : fk_oc_user_organization oc_role }|..|| oc_organization : fk_oc_role_organization oc_user_ref }|..|| oc_organization : fk_oc_user_ref_organization oc_service_registration }|..|| oc_host_registration : fk_oc_service_registration_host_registration oc_organization_node }|..|| oc_organization : fk_oc_organization_node_organization oc_user_role }|..|| oc_role : fk_oc_user_role_role_id oc_user_role }|..|| oc_user : fk_oc_user_role_user_id oc_user_ref_role }|..|| oc_role : fk_oc_user_ref_role_role_id oc_user_ref_role }|..|| oc_user_ref : fk_oc_user_ref_role_user_id oc_organization_property }|..|| oc_organization : fk_oc_organization_property_organization oc_series_elements }|..|| oc_series : fk_oc_series_elements_organization oc_assets_asset }|..|| oc_assets_snapshot : fk_oc_assets_asset_snapshot_id oc_workflow_operation }|..|| oc_workflow : fk_oc_workflow_operation_workflow_id oc_workflow_configuration }|..|| oc_workflow : fk_oc_workflow_configuration_workflow_id oc_workflow_operation_configuration }|..|| oc_workflow_operation : fk_oc_workflow_operation_configuration_workflow_operation_id oc_event_comment_reply }|..|| oc_event_comment : fk_oc_event_comment_reply_event_comment_id oc_user_action }|..|| oc_user_session : fk_oc_user_action_session_id ```

sergiobuj commented 1 year ago

Hi. I just started using mermaid (mmdc 10.2.2) and got the same error. I tried to reduce the diagram to get a minimal failing case and found that adding a comma (,) or a space (and potentially other characters) to the custom type raises the error.

That means that changing spaces like this works:

-                ~timestamp with time zone~ last_update "NOT NULL"
+                ~timestamp_with_time_zone~ last_update "NOT NULL"

The type ~numeric(4,2)~ rental_rate "NOT NULL" will make it fail, too, as it contains a comma. Without that character, it works.

I tried to go even further and tried to add a failing test to the codebase like:

  // file: mermaid/packages/mermaid/src/diagrams/er/parser/erDiagram.spec.js

  it('should allow an entity with attribute that has a generic type with complex name', function () {
    const entity = 'BOOK';
    const attribute1 = '~timestamp with time zone~ created_at';
    const attribute2 = '~numeric(4,2)~ rating';

    erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n}`);
    const entities = erDb.getEntities();
    expect(Object.keys(entities).length).toBe(1);
    expect(entities[entity].attributes.length).toBe(2);
    expect(entities[entity].attributes[0].attributeType).toBe('~timestamp with time zone~');
    expect(entities[entity].attributes[1].attributeType).toBe('~numeric(4,2)~');
  });

But the test passes fine. So the issue must be somewhere else, but I don't know much about the rendering process.

jcdang commented 11 months ago

Aside from preventing the recursion issue, what is the expected output for using generics in an ERD? @jgreywolf

Is it okay to support something like this instead of generics?

erDiagram
    MyEnt {
      "This is an attributeType with spaces" myAttributeName PK, FK "This is a comment"
    } 
jgreywolf commented 8 months ago

See most recent comment from @sidrus on #4937 about a workaround using #44; html entity