Это дополнение к ответу Каушика, в котором говорится (совершенно правильно, если не так много слов), что ваше утверждение полностью уязвимо для SQL-инъекций.
Я бы написал вашу процедуру следующим образом:
CREATE OR REPLACE PROCEDURE insert_or_upd_movement_baselines_planned_weight_proc(p_id IN VARCHAR2,
p_date IN DATE,
p_planned_col_name IN VARCHAR2,
p_planned_value IN NUMBER) AS
v_sql CLOB;
v_planned_col_name VARCHAR2(32);
BEGIN
v_planned_col_name := dbms_assert.simple_sql_name(p_planned_col_name);
v_sql := 'MERGE INTO movement_baselines tgt'||CHR(10)||
'USING (SELECT :p_id movement_id,'||CHR(10)||
' :p_date movement_date,'||CHR(10)||
' :p_planned_value planned_value'||CHR(10)||
' FROM dual) src'||CHR(10)||
'ON (tgt.movement_id = src.movement_id AND tgt.movement_date = src.movement_date)'||CHR(10)||
'WHEN NOT MATCHED THEN'||CHR(10)||
' INSERT (tgt.movement_id, tgt.movement_date, tgt.'||v_planned_col_name||')'||CHR(10)||
' VALUES (src.movement_id, src.movement_date, src.movement_date)'||CHR(10)||
'WHEN MATCHED THEN'||CHR(10)||
' UPDATE'||CHR(10)||
' SET tgt.'||v_planned_col_name||' = src.planned_value';
dbms_output.put_line('merge statement: ' || chr(10) || v_sql);
EXECUTE IMMEDIATE v_sql
USING p_id, p_date, p_planned_value;
END;
/
Обратите внимание на использование dbms_assert
для очистки вашего ввода - в этом случае мы проверяем, что значение, переданное вами в p_planned_col_name, соответствует требованиям для того, чтобы он был действительным идентификатором, что означает, что он определенноне может использоваться для внедрения SQL.
Кроме того, я переместил параметры в подзапрос, а это означает, что предложение using
немедленного выполнения теперь короче и, как мне кажется, яснее и проще в обслуживании.