2016-02-11 2 views
0

Я попытался использовать защищенный SQL-скрипт, используя this link, но когда динамические переменные связывания становятся все больше и больше, код кажется неуклюжим. Пожалуйста, проверьте этот код и дайте идею уменьшить сложность.Oracle Безопасное использование SQL-скрипта

На основе приведенного ниже кода вы можете увидеть эти три переменные (ZONE_CODE_STR, ACTIVE_STR и LOGICAL_ENV_STR) в качестве параметров для запроса, когда значение приходит, я использую их или заменяю их пустой строкой. Но проблема заключается в том, что я хотите добавить еще один параметр, логика IF-ELSE становится слишком сложной. Пожалуйста, помогите мне найти простой способ достичь, если я хочу добавить еще одну новую динамическую переменную.

Пример кода:

-- APPLICATION VARIABLES STARTS HERE 
    ZONE_CODE_STR VARCHAR2(100); 
    ZONE_CODE_FLAG VARCHAR2(20); 
    ACTIVE_STR VARCHAR2(100); 
    ACTIVE_FLAG VARCHAR2(20); 
    LOGICAL_ENV_STR VARCHAR2(100); 
    LOGICAL_ENV_FLAG VARCHAR2(20); 
    QUERY_STR VARCHAR2(4000); 
    -- APPLICATION VARIABLES END HERE 
BEGIN 



-- APPLICATION LOGIC STARTS HERE 
    ZONE_CODE_STR := ' AND ZONE_ENV_CODE IN (: IN_ZONE_ENV_CODE)'; 
    ZONE_CODE_FLAG := '@@[email protected]@'; 
    LOGICAL_ENV_STR := ' AND LOGICAL_DB_ENVIRONMENT.ZONE_ENV_CODE IN(: IN_ZONE_ENV_CODE)'; 
    LOGICAL_ENV_FLAG := '@@[email protected]@'; 
    ACTIVE_STR  := ' AND ACTIVE =: IN_ACTIVE'; 
    ACTIVE_FLAG := '@@[email protected]@'; 
    QUERY_STR :='SELECT 
RES_DETAIL.FRAME_NAME,RES_DETAIL.LISTENER_PORT, 
RES_DETAIL.PROPERTY_NAME,RES_DETAIL.PROPERTY_VALUE,RES_DETAIL.IP_ADDRESS, 
TEST_APP_ENTRIES.*, 
TEST_APP_WEB_SERVER_TYPE.APP_WEB_SERVER_TYPE_NAME, 
TEST_OPERATION_TYPE.OPERATION_TYPE_NAME, 
TEST_SINGLE_SIGN_ON.SINGLE_SIGN_ON_NAME,TEST_APP_REALM_ENTRIES.WARE_ID,TEST_APP_REALM_ENTRIES.RESOURCE_FILTER, 
TEST_APP_REALM_ENTRIES.PROTECTION_STATUS, TEST_APP_REALM_ENTRIES.AUTHENTICATION_TYPE_ID, 
TEST_AUTHENTICATION_TYPE.AUTHENTICATION_TYPE_NAME, 
TEST_APP_REALM_ENTRIES.ROLE 
FROM 
    (
    SELECT * FROM TEST_APP_ENTRIES WHERE APP_EXT_CODE =: IN_APP_EXT_CODE 
@@[email protected]@ 
@@[email protected]@ 
)TEST_APP_ENTRIES 
    INNER JOIN 
    TEST_APP_REALM_ENTRIES 
ON 
    TEST_APP_ENTRIES.WAE_ID = TEST_APP_REALM_ENTRIES.WAE_ID 

INNER JOIN 
    TEST_OPERATION_TYPE 
ON 
    TEST_APP_ENTRIES.OPERATION_TYPE_ID = TEST_OPERATION_TYPE.OPERATION_TYPE_ID 
INNER JOIN 
    TEST_APP_WEB_SERVER_TYPE 
ON 
    TEST_APP_ENTRIES.APP_WEB_SERVER_TYPE_ID = TEST_APP_WEB_SERVER_TYPE.APP_WEB_SERVER_TYPE_ID 
INNER JOIN 
    TEST_SINGLE_SIGN_ON 
ON 
    TEST_APP_ENTRIES.SINGLE_SIGN_ON_ID = TEST_SINGLE_SIGN_ON.SINGLE_SIGN_ON_ID 
INNER JOIN 
    TEST_AUTHENTICATION_TYPE 
ON 
    TEST_APP_REALM_ENTRIES.AUTHENTICATION_TYPE_ID = TEST_AUTHENTICATION_TYPE.AUTHENTICATION_TYPE_ID 
RIGHT OUTER JOIN 
    (SELECT APP_FRAMES.LOGICAL_DB_ENV_NAME AS LOGICAL_DB_ENV_NAME,APP_FRAME_PROPERTIES.FRAME_NAME AS FRAME_NAME,APP_FRAME_PROPERTIES.APP_EXT_CODE AS APP_EXT_CODE,FRAMES.LISTENER_PORT AS LISTENER_PORT, 
    LOGICAL_DB_ENVIRONMENT.ZONE_ENV_CODE AS ZONE_ENV_CODE, 
APP_FRAME_PROPERTIES.PROPERTY_NAME AS PROPERTY_NAME,APP_FRAME_PROPERTIES.PROPERTY_VALUE AS PROPERTY_VALUE,NODES.IP_ADDRESS AS IP_ADDRESS 
FROM APP_FRAME_PROPERTIES,NODES,FRAMES,APP_FRAMES,LOGICAL_DB_ENVIRONMENT 
WHERE APP_FRAME_PROPERTIES.FRAME_NAME = FRAMES.FRAME_NAME 
AND APP_FRAME_PROPERTIES.APP_EXT_CODE = APP_FRAMES.APP_EXT_CODE 
AND LOGICAL_DB_ENVIRONMENT.APP_EXT_CODE = APP_FRAMES.APP_EXT_CODE 
AND LOGICAL_DB_ENVIRONMENT.LOGICAL_DB_ENV_NAME = APP_FRAMES.LOGICAL_DB_ENV_NAME 
AND APP_FRAME_PROPERTIES.LOGICAL_DB_ENV_NAME = APP_FRAMES.LOGICAL_DB_ENV_NAME 
AND NODES.FRAME_NAME = APP_FRAME_PROPERTIES.FRAME_NAME 
AND APP_FRAMES.FRAME_NAME = APP_FRAME_PROPERTIES.FRAME_NAME 
AND APP_FRAMES.APP_EXT_CODE =: IN_APP_EXT_CODE 
@@[email protected]@ 
) RES_DETAIL 
ON 
    TEST_APP_ENTRIES.APP_EXT_CODE = RES_DETAIL.APP_EXT_CODE 
    AND TEST_APP_ENTRIES.ZONE_ENV_CODE = RES_DETAIL.ZONE_ENV_CODE ORDER BY TEST_APP_ENTRIES.WAE_ID,WARE_ID'; 

     IF(IN_ZONE_ENV_CODE IS NOT NULL AND IN_ZONE_ENV_CODE = 'UAT') THEN 
       IN_ZONE_ENV_CODE := 'DEV'; 
     ELSIF(IN_ZONE_ENV_CODE IS NOT NULL AND IN_ZONE_ENV_CODE = 'PROD') THEN 
       IN_ZONE_ENV_CODE := 'UAT'; 
     END IF; 

    IF(IN_ZONE_ENV_CODE IS NULL OR IN_ZONE_ENV_CODE = '' OR IN_ZONE_ENV_CODE = 'ALL') THEN 
       QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,''); 
       QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,''); 
       IF(IN_ACTIVE IS NULL OR IN_ACTIVE = '') THEN 
         QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,''); 
         OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_APP_EXT_CODE; 
       ELSE 
         QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,ACTIVE_STR); 
         OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ACTIVE,IN_APP_EXT_CODE; 
       END IF; 
     ELSIF (IN_ACTIVE IS NULL OR IN_ACTIVE = '') THEN 
         QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,''); 
         QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,ZONE_CODE_STR); 
         QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,LOGICAL_ENV_STR); 

         OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ZONE_ENV_CODE,IN_APP_EXT_CODE,IN_ZONE_ENV_CODE; 
     ELSE 
         QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,ZONE_CODE_STR); 
         QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,LOGICAL_ENV_STR); 
         QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,ACTIVE_STR); 

         OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ZONE_ENV_CODE,IN_ACTIVE,IN_APP_EXT_CODE,IN_ZONE_ENV_CODE; 
     END IF; 
+1

Хотя я прочел это, я подумал, что должен хотя бы спросить, есть ли причина, по которой вы не используете фактические связанные параметры со статическим SQL-запросом. Я довольно уверен, что вы хотите сделать, это может быть выполнено с помощью параметризованного запроса, а не для совместного объединения динамически построенных SQL. – gmiley

+0

Да, я выбираю параметризованный запрос, а не конкатенацию для безопасного выполнения запроса. – sunleo

+2

Вы используете 'REPLACE' для замены кода, чтобы заменить переменную place-holder. Это не параметризуется. Вы все еще, в основном, объединяете запрос вместе, и это никоим образом не безопасно. Найдите google для 'using oracle bind variables' для запуска. – gmiley

ответ

2

Я думаю, что вы могли бы сделать то, что вы после того, как с помощью следующего статического SQL:

select res_detail.frame_name, 
     res_detail.listener_port, 
     res_detail.property_name, 
     res_detail.property_value, 
     res_detail.ip_address, 
     test_app_entries.*, 
     test_app_web_server_type.app_web_server_type_name, 
     test_operation_type.operation_type_name, 
     test_single_sign_on.single_sign_on_name, 
     test_app_realm_entries.ware_id, 
     test_app_realm_entries.resource_filter, 
     test_app_realm_entries.protection_status, 
     test_app_realm_entries.authentication_type_id, 
     test_authentication_type.authentication_type_name, 
     test_app_realm_entries.role 
from (select * 
     from test_app_entries 
     where app_ext_code = in_app_ext_code 
     and (zone_env_code = in_zone_env_code or in_zone_env_code is null) -- changed 
     and (active = in_active or in_active is null)      -- changed 
     ) test_app_entries 
     inner join test_app_realm_entries on test_app_entries.wae_id = test_app_realm_entries.wae_id 
     inner join test_operation_type on test_app_entries.operation_type_id = test_operation_type.operation_type_id 
     inner join test_app_web_server_type on test_app_entries.app_web_server_type_id = test_app_web_server_type.app_web_server_type_id 
     inner join test_single_sign_on on test_app_entries.single_sign_on_id = test_single_sign_on.single_sign_on_id 
     inner join test_authentication_type on test_app_realm_entries.authentication_type_id = test_authentication_type.authentication_type_id 
     right outer join (select app_frames.logical_db_env_name as logical_db_env_name, 
           app_frame_properties.frame_name as frame_name, 
           app_frame_properties.app_ext_code as app_ext_code, 
           frames.listener_port as listener_port, 
           logical_db_environment.zone_env_code as zone_env_code, 
           app_frame_properties.property_name as property_name, 
           app_frame_properties.property_value as property_value, 
           nodes.ip_address as ip_address 
         from app_frame_properties, 
           nodes, 
           frames, 
           app_frames, 
           logical_db_environment 
         where app_frame_properties.frame_name = frames.frame_name 
         and app_frame_properties.app_ext_code = app_frames.app_ext_code 
         and logical_db_environment.app_ext_code = app_frames.app_ext_code 
         and logical_db_environment.logical_db_env_name = app_frames.logical_db_env_name 
         and app_frame_properties.logical_db_env_name = app_frames.logical_db_env_name 
         and nodes.frame_name = app_frame_properties.frame_name 
         and app_frames.frame_name = app_frame_properties.frame_name 
         and app_frames.app_ext_code = in_app_ext_code 
         and (logical_db_environment.zone_env_code = in_zone_env_code or in_zone_env_code is null) -- changed 
         ) res_detail on test_app_entries.app_ext_code = res_detail.app_ext_code 
             and test_app_entries.zone_env_code = res_detail.zone_env_code 
order by test_app_entries.wae_id, ware_id; 

Так где вы были ранее тестирования для параметров является нулевым или нет, а затем построив это там, где это необходимо, вместо этого я заменил их на (parameter = some_col or parameter is null)

Вам нужно будет проверить свои данные, чтобы убедиться, что производительность по-прежнему удовлетворительна.

Дополнительные примечания:

  1. Я хотел бы использовать короткие псевдонимы таблиц вместо ваших полных имен таблиц; это более понятно.
  2. Вы используете синтаксис соединения ANSI SQL для соединений внешнего запроса, но в подзапросе res_detail используются старые сочетания стиля. Переключите это, чтобы использовать синтаксис соединения ANSI SQL.
  3. ware_id в порядке отсутствует отсутствует псевдоним. Из какой таблицы это получается?
  4. test_app_entries.* - в производственном коде, это плохая идея использовать * во внешнем выборе; что произойдет, если кто-то добавит столбец?

В общем, лучше избегать динамического sql, если это вообще возможно - у вас не будет проблем с безопасностью, если вы не будете конкатенировать дополнительный текст в свой SQL-код, а в целом проще поддерживать и отлаживать ,

+0

Спасибо за ваш ответ. – sunleo

+0

N.B. Где я использовал '(параметр = some_col или параметр null)' вполне возможно, что вы могли бы использовать 'nvl (parameter, some_col) = some_col' (как предлагалось @gmiley) и получить преимущества производительности от условной фильтрации sql, которые Oracle может но имейте в виду, что если у вас есть нули в столбце, с которым вы сравниваете, тогда вы вполне можете получить недостающие строки. См. Статью Джонатана Льюиса для более подробной информации. (Https://jonathanlewis.wordpress.com/2007/01/09/conditional-sql/) – Boneist

1

В вашем PL SQL выше, вы являются использованием BIND VARIABLES в некоторые из них, тем не менее, вы по-прежнему создает динамический SQL с использованием конкатенации (в данном случае с помощью функции REPLACE). Запрошенный вами запрос довольно большой и сложный, поэтому я буду использовать только упрощенный выбор, чтобы дать вам пример.

-- Your stripped-down code: 
ZONE_CODE_STR := ' AND ZONE_ENV_CODE IN (:IN_ZONE_ENV_CODE)'; 
ZONE_CODE_FLAG := '@@[email protected]@'; 
ACTIVE_STR  := ' AND ACTIVE = :IN_ACTIVE '; 
ACTIVE_FLAG := '@@[email protected]@'; 
QUERY_STR :='SELECT * FROM TEST_APP_ENTRIES WHERE APP_EXT_CODE = :IN_APP_EXT_CODE @@[email protected]@ @@[email protected]@ '; 

IF(IN_ZONE_ENV_CODE IS NOT NULL AND IN_ZONE_ENV_CODE = 'UAT') THEN 
    IN_ZONE_ENV_CODE := 'DEV'; 
ELSIF(IN_ZONE_ENV_CODE IS NOT NULL AND IN_ZONE_ENV_CODE = 'PROD') THEN 
    IN_ZONE_ENV_CODE := 'UAT'; 
END IF; 

IF(IN_ZONE_ENV_CODE IS NULL OR IN_ZONE_ENV_CODE = '' OR IN_ZONE_ENV_CODE = 'ALL') THEN 
    QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,''); 
    QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,''); 
    IF(IN_ACTIVE IS NULL OR IN_ACTIVE = '') THEN 
     QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,''); 
     OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_APP_EXT_CODE; 
    ELSE 
     QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,ACTIVE_STR); 
     OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ACTIVE,IN_APP_EXT_CODE; 
    END IF; 
ELSIF (IN_ACTIVE IS NULL OR IN_ACTIVE = '') THEN 
    QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,''); 
    QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,ZONE_CODE_STR); 
    QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,LOGICAL_ENV_STR); 
    OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ZONE_ENV_CODE,IN_APP_EXT_CODE,IN_ZONE_ENV_CODE; 
ELSE 
    QUERY_STR := REPLACE(QUERY_STR,ZONE_CODE_FLAG,ZONE_CODE_STR); 
    QUERY_STR := REPLACE(QUERY_STR,LOGICAL_ENV_FLAG,LOGICAL_ENV_STR); 
    QUERY_STR := REPLACE(QUERY_STR,ACTIVE_FLAG,ACTIVE_STR); 
    OPEN OUT_RESULT FOR QUERY_STR USING IN_APP_EXT_CODE,IN_ZONE_ENV_CODE,IN_ACTIVE,IN_APP_EXT_CODE,IN_ZONE_ENV_CODE; 
END IF; 

То, что я предлагаю сделать это, чтобы реорганизовать ваш фактический SQL запрос, чтобы избавиться от многих, если не все, дополнительный PL/SQL кода, связанного с имея необходимость переписывать запрос динамически ...В коде ниже я объясню, что происходит:

SELECT * FROM TEST_APP_ENTRIES 
WHERE APP_EXT_CODE = IN_APP_EXT_CODE 
AND CASE Coalesce(:IN_ZONE_ENV_CODE, 'ALL') -- See Note 1 below... 
     WHEN 'ALL' THEN ZONE_ENV_CODE 
     ELSE :IN_ZONE_ENV_CODE 
    END = ZONE_ENV_CODE 
AND Coalesce(:IN_ACTIVE, ACTIVE) = ACTIVE; -- See Note 2 below... 
  • Примечание 1: Вместо проверки переменного быть нулевыми, а затем заменить местоблюститель, мы можем просто использовать функцию COALESCE (который возвращает первый ненулевой элемент в списке, разделенном запятыми), и сравните это в части WHEN с литеральным значением 'ALL' (которое мы будем оценивать TRUE при сравнении, если переменная :IN_ACTIVE равна либо NULL, либо фактически содержит значение ALL), и мы будем использовать ZONE_ENV_CODE, в противном случае, если это не оценивается TRUE переходим к ELSE участку WHEN и вместо этого используем :IN_ZONE_ENV_CODE. Затем один из этих двух будет сравнивать с ZONE_ENV_CODE. Это означает, что если :IN_ZONE_ENV_CODE является либо NULL, либо ALL, мы используем ZONE_ENV_CODE = ZONE_ENV_CODE в предложении where, в противном случае вместо этого мы используем :IN_ZONE_ENV_CODE = ZONE_ENV_CODE.

  • Примечание 2: сходно, но гораздо проще ... Мы просто используем COALESCE для сравнения ACTIVE = ACTIVE если :IN_ACTIVE является NULL, в противном случае :IN_ACTIVE = ACTIVE.

+0

Спасибо, но как вы будете идентифицировать никакие параметры в OPEN OUT_RESULT ДЛЯ QUERY_STR ИСПОЛЬЗОВАНИЯ? ?Вот???? динамически – sunleo

+0

Вы будете передавать все параметры каждый раз, это просто, что некоторые будут «NULL», так? Этот 'NULL' будет обрабатываться функцией COALESCE' для этого параметра. – gmiley

+0

Хорошо, я попробую, Спасибо за помощь. – sunleo

Смежные вопросы