From 54fe17e49dcc5a5f745808d5303484783b82898d Mon Sep 17 00:00:00 2001 From: Jan Kvetina Date: Sun, 6 Mar 2022 10:54:10 +0100 Subject: [PATCH] Navigation performance improved by using MVW --- apex/f770/application/pages/page_00910.sql | 47 ++++- packages/app.sql | 33 +-- views/nav_availability_mvw.sql | 36 ++++ views/nav_overview.sql | 225 ++------------------- views/nav_overview_mvw.sql | 218 ++++++++++++++++++++ 5 files changed, 325 insertions(+), 234 deletions(-) create mode 100644 views/nav_availability_mvw.sql create mode 100644 views/nav_overview_mvw.sql diff --git a/apex/f770/application/pages/page_00910.sql b/apex/f770/application/pages/page_00910.sql index bb47288..a293b97 100644 --- a/apex/f770/application/pages/page_00910.sql +++ b/apex/f770/application/pages/page_00910.sql @@ -23,7 +23,7 @@ wwv_flow_api.create_page( ,p_required_role=>wwv_flow_api.id(9556407311505078) ,p_page_comment=>'Navigation setup page' ,p_last_updated_by=>'DEV' -,p_last_upd_yyyymmddhh24miss=>'20220305173721' +,p_last_upd_yyyymmddhh24miss=>'20220306094513' ); wwv_flow_api.create_page_plug( p_id=>wwv_flow_api.id(24141119524306145) @@ -3129,6 +3129,17 @@ wwv_flow_api.create_page_button( ,p_button_css_classes=>'&P910_AUTO_UPDATE_HOT.' ,p_button_cattributes=>'style="margin-left: 2rem;"' ); +wwv_flow_api.create_page_button( + p_id=>wwv_flow_api.id(32964041787841115) +,p_button_sequence=>30 +,p_button_plug_id=>wwv_flow_api.id(9192009232668637) +,p_button_name=>'PUBLISH_CHANGES' +,p_button_action=>'SUBMIT' +,p_button_template_options=>'#DEFAULT#' +,p_button_template_id=>wwv_flow_api.id(9145249029569999) +,p_button_image_alt=>'&BUTTON_PUBLISH_CHANGES.' +,p_button_position=>'RIGHT_OF_TITLE' +); wwv_flow_api.create_page_item( p_id=>wwv_flow_api.id(9260980865429010) ,p_name=>'P910_ADD_PAGE' @@ -3268,16 +3279,36 @@ wwv_flow_api.create_page_process( ,p_process_sequence=>20 ,p_process_point=>'AFTER_SUBMIT' ,p_process_type=>'NATIVE_PLSQL' -,p_process_name=>'AUTO_UPDATE' +,p_process_name=>'ACTION_AUTO_UPDATE' ,p_process_sql_clob=>wwv_flow_string.join(wwv_flow_t_varchar2( 'app.log_action(''AUTO_UPDATE'');', '--', 'app_actions.nav_autoupdate();', +'--', +'app.log_success();', '')) ,p_process_clob_language=>'PLSQL' ,p_error_display_location=>'INLINE_IN_NOTIFICATION' ,p_process_when_button_id=>wwv_flow_api.id(9261437105429015) ); +wwv_flow_api.create_page_process( + p_id=>wwv_flow_api.id(32964351219841118) +,p_process_sequence=>30 +,p_process_point=>'AFTER_SUBMIT' +,p_process_type=>'NATIVE_PLSQL' +,p_process_name=>'ACTION_PUBLISH_CHANGES' +,p_process_sql_clob=>wwv_flow_string.join(wwv_flow_t_varchar2( +'app.log_action(''PUBLISH_CHANGES'');', +'--', +'DBMS_MVIEW.REFRESH(''NAV_AVAILABILITY_MVW'', ''C'', parallelism => 2);', +'DBMS_MVIEW.REFRESH(''NAV_OVERVIEW_MVW'', ''C'', parallelism => 2);', +'--', +'app.log_success();', +'')) +,p_process_clob_language=>'PLSQL' +,p_error_display_location=>'INLINE_IN_NOTIFICATION' +,p_process_when_button_id=>wwv_flow_api.id(32964041787841115) +); wwv_flow_api.create_page_process( p_id=>wwv_flow_api.id(9261288538429013) ,p_process_sequence=>10 @@ -3310,6 +3341,18 @@ wwv_flow_api.create_page_process( ,p_process_when=>'P910_REMOVE_PAGE' ,p_process_when_type=>'ITEM_IS_NOT_NULL' ); +wwv_flow_api.component_end; +end; +/ +begin +wwv_flow_api.component_begin ( + p_version_yyyy_mm_dd=>'2021.04.15' +,p_release=>'21.1.7' +,p_default_workspace_id=>9014660246496943 +,p_default_application_id=>770 +,p_default_id_offset=>0 +,p_default_owner=>'CORE' +); wwv_flow_api.create_page_process( p_id=>wwv_flow_api.id(22086431917580632) ,p_process_sequence=>30 diff --git a/packages/app.sql b/packages/app.sql index cf65bb3..e40f9a1 100644 --- a/packages/app.sql +++ b/packages/app.sql @@ -1195,32 +1195,15 @@ CREATE OR REPLACE PACKAGE BODY app AS BEGIN -- get auth cheme, procedure... SELECT - MIN(p.authorization_scheme), - MIN(f.package_name), -- package_name - MIN(f.object_name), -- procedure_name - MIN(f.pls_type), - MIN(a.argument_name) -- argument_name + n.auth_scheme, + n.package_name, + n.procedure_name, + n.data_type, + n.argument_name INTO v_auth_scheme, v_package_name, v_procedure_name, v_data_type, v_page_argument - FROM apex_application_pages p - LEFT JOIN user_procedures s - ON s.object_name IN ('A' || TO_CHAR(in_app_id), 'APP', 'AUTH') -- packages - AND s.procedure_name = p.authorization_scheme - LEFT JOIN user_arguments f - ON f.object_name = s.procedure_name - AND f.package_name = s.object_name - AND f.overload IS NULL - AND f.position = 0 - AND f.argument_name IS NULL - AND f.in_out = 'OUT' - LEFT JOIN user_arguments a - ON a.object_name = f.package_name - AND a.package_name = f.object_name - AND a.overload IS NULL - AND a.position = 1 - AND a.data_type = 'NUMBER' - AND a.in_out = 'IN' - WHERE p.application_id = in_app_id - AND p.page_id = in_page_id; + FROM nav_availability_mvw n + WHERE n.application_id = in_app_id + AND n.page_id = in_page_id; -- log current page IF app.is_debug_on() AND in_page_id = app.get_page_id() THEN diff --git a/views/nav_availability_mvw.sql b/views/nav_availability_mvw.sql new file mode 100644 index 0000000..f1ebfcb --- /dev/null +++ b/views/nav_availability_mvw.sql @@ -0,0 +1,36 @@ +--DROP MATERIALIZED VIEW nav_availability_mvw; +CREATE MATERIALIZED VIEW nav_availability_mvw +BUILD DEFERRED +REFRESH ON DEMAND COMPLETE +AS +SELECT + p.application_id, + p.page_id, + MIN(p.authorization_scheme) AS auth_scheme, + MIN(f.package_name) AS package_name, + MIN(f.object_name) AS procedure_name, + MIN(f.pls_type) AS data_type, + MIN(a.argument_name) AS argument_name +FROM apex_application_pages p +LEFT JOIN all_procedures s + ON s.owner = app.get_core_owner() + AND s.object_name IN ('A' || TO_CHAR(p.application_id), 'APP', 'AUTH') -- packages + AND s.procedure_name = p.authorization_scheme +LEFT JOIN all_arguments f + ON f.owner = s.owner + AND f.object_name = s.procedure_name + AND f.package_name = s.object_name + AND f.overload IS NULL + AND f.position = 0 + AND f.argument_name IS NULL + AND f.in_out = 'OUT' +LEFT JOIN all_arguments a + ON a.owner = f.owner + AND a.object_name = f.package_name + AND a.package_name = f.object_name + AND a.overload IS NULL + AND a.position = 1 + AND a.data_type = 'NUMBER' + AND a.in_out = 'IN' +GROUP BY p.application_id, p.page_id; + diff --git a/views/nav_overview.sql b/views/nav_overview.sql index e9c737c..5856887 100644 --- a/views/nav_overview.sql +++ b/views/nav_overview.sql @@ -5,154 +5,33 @@ WITH x AS ( app.get_app_id() AS app_id, app.get_core_app_id() AS core_app_id FROM DUAL -), -t AS ( - SELECT - ROWNUM AS r#, -- to keep hierarchy sorted - t.* - FROM ( - SELECT - n.app_id, - n.page_id, - n.order#, - -- - REPLACE(p.page_name, '&' || 'APP_NAME.', a.application_name) AS page_name, - REPLACE(p.page_title, '&' || 'APP_NAME.', a.application_name) AS page_title, - -- - p.page_alias, - p.page_group, - p.authorization_scheme, - p.page_css_classes, - p.page_mode, - p.page_template, - p.page_comment AS comments, - -- - '#' AS javascript_target, - i.item_source AS javascript, - -- - LEVEL - 1 AS depth, - CONNECT_BY_ROOT NVL(n.order#, n.page_id) AS page_root - FROM navigation n - JOIN apps a - ON a.app_id = n.app_id - CROSS JOIN x - LEFT JOIN apex_application_pages p - ON p.application_id = n.app_id - AND p.page_id = n.page_id - LEFT JOIN apex_applications a - ON a.application_id = p.application_id - LEFT JOIN apex_application_page_items i - ON i.application_id = n.app_id - AND i.item_name = 'P' || TO_CHAR(n.page_id) || '_JAVASCRIPT_TARGET' - WHERE (n.app_id, n.page_id) NOT IN ( - SELECT - app.get_core_app_id() AS app_id, - 947 AS page_id - FROM DUAL - ) - CONNECT BY n.parent_id = PRIOR n.page_id - AND n.app_id = PRIOR n.app_id - START WITH n.parent_id IS NULL - ORDER SIBLINGS BY n.app_id, n.order#, n.page_id - ) t ) SELECT n.app_id, n.page_id, n.parent_id, n.order#, - -- - t.page_root || ' ' || COALESCE ( - t.page_group, - ( - SELECT t.page_group - FROM t - WHERE t.app_id = n.app_id - AND t.page_id = n.parent_id - ) - ) AS page_group, - -- - t.page_alias, - -- - CASE WHEN r.page_id IS NULL - THEN REPLACE(LTRIM(RPAD('-', t.depth * 4), '-'), ' ', '&' || 'nbsp; ') || - app.get_page_name ( - in_app_id => n.app_id, - in_page_id => n.page_id, - in_name => t.page_name - ) - END AS page_name, - -- - app.get_page_title ( - in_app_id => n.app_id, - in_page_id => n.page_id, - in_title => t.page_title - ) AS page_title, - -- - t.page_css_classes AS css_class, - t.page_template, - -- + n.page_group, + n.page_alias, + n.page_name, + n.page_title, + n.css_class, + n.page_template, n.is_hidden, n.is_reset, n.is_shared, - -- - CASE WHEN t.page_mode = 'Normal' THEN NULL ELSE 'Y' END AS is_modal, - CASE WHEN t.javascript IS NOT NULL THEN 'Y' END AS is_javascript, - t.javascript, - -- - CASE - WHEN t.authorization_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' - THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') - -- - WHEN t.authorization_scheme IS NULL AND n.page_id NOT IN (0, 9999) - THEN app.get_icon('fa-warning', 'Auth scheme is missing') - -- - ELSE app_actions.get_html_a(app.get_page_url ( - in_page_id => 920, - in_app_id => n.app_id, - in_names => 'P920_AUTH_SCHEME', - in_values => t.authorization_scheme - ), t.authorization_scheme) - END AS auth_scheme, - -- - CASE - WHEN t.javascript_target IS NOT NULL - THEN t.javascript_target - -- - WHEN n.page_id > 0 AND r.page_id IS NULL - THEN app.get_page_url ( - in_page_id => n.page_id, - in_app_id => n.app_id, - in_session_id => CASE WHEN n.page_id = 9999 THEN 0 END - ) - END AS page_url, - -- - t.comments, - -- - 'UD' AS allow_changes, -- U = update, D = delete - -- - t.page_root || '.' || TO_CHAR(10000 + t.r#) || '.' || NVL(t.order#, t.page_id) || '.' || n.page_id AS sort_order, - -- - CASE - WHEN r.page_id IS NOT NULL - THEN app.get_icon('fa-minus-square', 'Remove record from Navigation table') - END AS action, - -- - app.get_page_url ( - in_page_id => x.page_id, - in_app_id => x.core_app_id, - in_names => 'P' || TO_CHAR(x.page_id) || '_REMOVE_PAGE', - in_values => TO_CHAR(n.page_id) - ) AS action_url -FROM navigation n -JOIN apps a - ON a.app_id = n.app_id + n.is_modal, + n.is_javascript, + n.javascript, + n.auth_scheme, + n.page_url, + n.comments, + n.allow_changes, + n.sort_order, + n.action, + n.action_url +FROM nav_overview_mvw n CROSS JOIN x -LEFT JOIN t - ON t.app_id = n.app_id - AND t.page_id = n.page_id -LEFT JOIN nav_pages_to_remove r - ON r.page_id = n.page_id WHERE ( n.app_id = x.app_id OR ( @@ -164,75 +43,7 @@ WHERE ( WHERE n.app_id = x.app_id ) ) -) --- -UNION ALL -SELECT - n.app_id, - n.page_id, - n.parent_id, - n.order#, - NVL(t.page_root, n.page_id) || ' ' || n.page_group AS page_group, - n.page_alias, - -- - CASE WHEN n.parent_id IS NOT NULL - THEN REPLACE(LTRIM(RPAD('-', (t.depth + 1) * 4), '-'), ' ', '&' || 'nbsp; ') - END || app.get_page_name(in_app_id => n.app_id, in_page_id => n.page_id, in_name => n.page_name) AS page_name, - -- - n.page_title, - n.css_class, - n.page_template, - -- - n.is_hidden, - n.is_reset, - n.is_shared, - -- - CASE WHEN n.page_mode = 'Normal' THEN NULL ELSE 'Y' END AS is_modal, - CASE WHEN t.javascript IS NOT NULL THEN 'Y' END AS is_javascript, - t.javascript, - -- - CASE WHEN n.auth_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' - THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') - ELSE n.auth_scheme - END AS auth_scheme, - -- - CASE - WHEN t.javascript_target IS NOT NULL - THEN t.javascript_target - -- - ELSE app.get_page_url ( - in_page_id => n.page_id, - in_app_id => n.app_id - ) - END AS page_url, - -- - n.comments, - -- - NULL AS allow_changes, -- no changes allowed - -- - NVL(t.page_root, n.page_id) || '.' || TO_CHAR(10000 + ( - SELECT NVL(MAX(z.r#), 0) AS nearest_r# - FROM t z - WHERE z.app_id = n.app_id - AND z.page_group = n.page_group - AND z.order# = n.order# - )) || '.' || NVL(n.order#, n.page_id) AS sort_order, - -- - app.get_icon('fa-plus-square', 'Create record in Navigation table') AS action, - -- - app.get_page_url ( - in_page_id => x.page_id, - in_app_id => x.core_app_id, - in_names => 'P' || TO_CHAR(x.page_id) || '_ADD_PAGE', - in_values => TO_CHAR(n.page_id) - ) AS action_url -FROM nav_pages_to_add n -JOIN apps a - ON a.app_id = n.app_id -CROSS JOIN x -LEFT JOIN t - ON t.app_id = n.app_id - AND t.page_id = n.parent_id; +); -- COMMENT ON TABLE nav_overview IS '[CORE - DASHBOARD] Enriched navigation overview used also for menu rendering'; -- diff --git a/views/nav_overview_mvw.sql b/views/nav_overview_mvw.sql new file mode 100644 index 0000000..965be98 --- /dev/null +++ b/views/nav_overview_mvw.sql @@ -0,0 +1,218 @@ +--DROP MATERIALIZED VIEW nav_overview_mvw; +CREATE MATERIALIZED VIEW nav_overview_mvw +BUILD DEFERRED +REFRESH ON DEMAND COMPLETE +AS +WITH t AS ( + SELECT /*+ MATERIALIZE */ + ROWNUM AS r#, -- to keep hierarchy sorted + t.* + FROM ( + SELECT + n.app_id, + n.page_id, + n.order#, + -- + REPLACE(p.page_name, '&' || 'APP_NAME.', a.application_name) AS page_name, + REPLACE(p.page_title, '&' || 'APP_NAME.', a.application_name) AS page_title, + -- + p.page_alias, + p.page_group, + p.authorization_scheme, + p.page_css_classes, + p.page_mode, + p.page_template, + p.page_comment AS comments, + -- + '#' AS javascript_target, + i.item_source AS javascript, + -- + LEVEL - 1 AS depth, + CONNECT_BY_ROOT NVL(n.order#, n.page_id) AS page_root + FROM navigation n + JOIN apps a + ON a.app_id = n.app_id + LEFT JOIN apex_application_pages p + ON p.application_id = n.app_id + AND p.page_id = n.page_id + LEFT JOIN apex_applications a + ON a.application_id = p.application_id + LEFT JOIN apex_application_page_items i + ON i.application_id = n.app_id + AND i.item_name = 'P' || TO_CHAR(n.page_id) || '_JAVASCRIPT_TARGET' + CONNECT BY n.parent_id = PRIOR n.page_id + AND n.app_id = PRIOR n.app_id + START WITH n.parent_id IS NULL + ORDER SIBLINGS BY n.app_id, n.order#, n.page_id + ) t +) +SELECT + n.app_id, + n.page_id, + n.parent_id, + n.order#, + -- + t.page_root || ' ' || COALESCE ( + t.page_group, + ( + SELECT t.page_group + FROM t + WHERE t.app_id = n.app_id + AND t.page_id = n.parent_id + ) + ) AS page_group, + -- + t.page_alias, + -- + CASE WHEN r.page_id IS NULL + THEN REPLACE(LTRIM(RPAD('-', t.depth * 4), '-'), ' ', '&' || 'nbsp; ') || + app.get_page_name ( + in_app_id => n.app_id, + in_page_id => n.page_id, + in_name => t.page_name + ) + END AS page_name, + -- + app.get_page_title ( + in_app_id => n.app_id, + in_page_id => n.page_id, + in_title => t.page_title + ) AS page_title, + -- + t.page_css_classes AS css_class, + t.page_template, + -- + n.is_hidden, + n.is_reset, + n.is_shared, + -- + CASE WHEN t.page_mode = 'Normal' THEN NULL ELSE 'Y' END AS is_modal, + CASE WHEN t.javascript IS NOT NULL THEN 'Y' END AS is_javascript, + t.javascript, + -- + CASE + WHEN t.authorization_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' + THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') + -- + WHEN t.authorization_scheme IS NULL AND n.page_id NOT IN (0, 9999) + THEN app.get_icon('fa-warning', 'Auth scheme is missing') + -- + ELSE app_actions.get_html_a(app.get_page_url ( + in_page_id => 920, + in_app_id => n.app_id, + in_names => 'P920_AUTH_SCHEME', + in_values => t.authorization_scheme + ), t.authorization_scheme) + END AS auth_scheme, + -- + CASE + WHEN t.javascript_target IS NOT NULL + THEN t.javascript_target + -- + WHEN n.page_id > 0 AND r.page_id IS NULL + THEN app.get_page_url ( + in_page_id => n.page_id, + in_app_id => n.app_id, + in_session_id => CASE WHEN n.page_id = 9999 THEN 0 END + ) + END AS page_url, + -- + t.comments, + -- + 'UD' AS allow_changes, -- U = update, D = delete + -- + t.page_root || '.' || TO_CHAR(10000 + t.r#) || '.' || NVL(t.order#, t.page_id) || '.' || n.page_id AS sort_order, + -- + CASE + WHEN r.page_id IS NOT NULL + THEN app.get_icon('fa-minus-square', 'Remove record from Navigation table') + END AS action, + -- + app.get_page_url ( + in_page_id => 910,--x.page_id, + in_app_id => app.get_core_app_id(), + in_names => 'P' || TO_CHAR(910) || '_REMOVE_PAGE', + in_values => TO_CHAR(n.page_id) + ) AS action_url +FROM navigation n +JOIN apps a + ON a.app_id = n.app_id +LEFT JOIN t + ON t.app_id = n.app_id + AND t.page_id = n.page_id +LEFT JOIN nav_pages_to_remove r + ON r.page_id = n.page_id +WHERE (n.app_id, n.page_id) NOT IN ( + SELECT + app.get_core_app_id() AS app_id, + 947 AS page_id + FROM DUAL +) +-- +UNION ALL +SELECT + n.app_id, + n.page_id, + n.parent_id, + n.order#, + NVL(t.page_root, n.page_id) || ' ' || n.page_group AS page_group, + n.page_alias, + -- + CASE WHEN n.parent_id IS NOT NULL + THEN REPLACE(LTRIM(RPAD('-', (t.depth + 1) * 4), '-'), ' ', '&' || 'nbsp; ') + END || app.get_page_name(in_app_id => n.app_id, in_page_id => n.page_id, in_name => n.page_name) AS page_name, + -- + n.page_title, + n.css_class, + n.page_template, + -- + n.is_hidden, + n.is_reset, + n.is_shared, + -- + CASE WHEN n.page_mode = 'Normal' THEN NULL ELSE 'Y' END AS is_modal, + CASE WHEN t.javascript IS NOT NULL THEN 'Y' END AS is_javascript, + t.javascript, + -- + CASE WHEN n.auth_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' + THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') + ELSE n.auth_scheme + END AS auth_scheme, + -- + CASE + WHEN t.javascript_target IS NOT NULL + THEN t.javascript_target + -- + ELSE app.get_page_url ( + in_page_id => n.page_id, + in_app_id => n.app_id + ) + END AS page_url, + -- + n.comments, + -- + NULL AS allow_changes, -- no changes allowed + -- + NVL(t.page_root, n.page_id) || '.' || TO_CHAR(10000 + ( + SELECT NVL(MAX(z.r#), 0) AS nearest_r# + FROM t z + WHERE z.app_id = n.app_id + AND z.page_group = n.page_group + AND z.order# = n.order# + )) || '.' || NVL(n.order#, n.page_id) AS sort_order, + -- + app.get_icon('fa-plus-square', 'Create record in Navigation table') AS action, + -- + app.get_page_url ( + in_page_id => 910, + in_app_id => app.get_core_app_id(), + in_names => 'P' || TO_CHAR(910) || '_ADD_PAGE', + in_values => TO_CHAR(n.page_id) + ) AS action_url +FROM nav_pages_to_add n +JOIN apps a + ON a.app_id = n.app_id +LEFT JOIN t + ON t.app_id = n.app_id + AND t.page_id = n.parent_id; +