如何在存储过程中处理逗号分隔的输入
How to process comma separated input in stored procedure
我需要编写一个程序,其中将 table 名称作为输入参数(一次应该只有一个 table)和列名(应该有多个列名逗号分隔)和列值(应该有多个列名逗号分隔)。
我的尝试:
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN VARCHAR2,-- It can have multiple column names separated by comma
p_column_value IN VARCHAR2-- It can have multiple column names separated by
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '||p_table_name||'should have multiple column names' ||
'VALUES('||'should have multiple column names' ||')';
EXECUTE IMMEDIATE lv_str;
END;
Tool used: SQL Developer(18c)
我对如何在过程主体内处理多个列名和列值感到困惑。我将如何定义一个数组并相应地进行?
不要。您正在为自己设置易受 SQL 注入攻击的程序。
如果你真的想要(请不要):
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN VARCHAR2,
p_column_value IN VARCHAR2
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '||p_table_name||' (' || p_column_name || ') VALUES(' || p_column_value ||')';
EXECUTE IMMEDIATE lv_str;
END;
/
那么你可以这样做:
BEGIN
sp_test_insert(
'my_table',
'col1, col2, col3',
q'['a', DATE '2022-05-31', 42]'
);
END;
/
但你也可以这样做:
BEGIN
sp_test_insert(
'my_table',
'col1, col2, col3',
q'['a', (SELECT DATE '1970-01-01' FROM secret_table WHERE username = 'Admin' AND password_hash = 'abcgefg1234'), 42]'
);
END;
/
不要让您的应用程序容易受到 SQL 注入攻击;尽量避免动态 SQL!
如果你想让它更能抵抗 SQL 注入攻击,那么你可以使用 DBMS_ASSERT
包:
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN SYS.ODCIVARCHAR2LIST,
p_column_value IN SYS.ODCIVARCHAR2LIST
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '
|| DBMS_ASSERT.SQL_OBJECT_NAME(
DBMS_ASSERT.ENQUOTE_NAME(p_table_name, FALSE)
)
||' ('
|| DBMS_ASSERT.ENQUOTE_NAME(p_column_name(1), FALSE);
FOR i IN 2 .. p_column_name.COUNT LOOP
lv_str := lv_str || ', '
|| DBMS_ASSERT.ENQUOTE_NAME(p_column_name(i), FALSE);
END LOOP;
lv_str := lv_str || ') VALUES('
|| DBMS_ASSERT.ENQUOTE_LITERAL(p_column_value(1));
FOR i IN 2 .. p_column_name.COUNT LOOP
lv_str := lv_str || ', '
|| DBMS_ASSERT.ENQUOTE_LITERAL(p_column_value(i));
END LOOP;
lv_str := lv_str || ')';
EXECUTE IMMEDIATE lv_str;
END;
/
然后:
BEGIN
sp_test_insert(
'MY_TABLE',
SYS.ODCIVARCHAR2LIST( 'COL1', 'COL2', 'COL3'),
SYS.ODCIVARCHAR2LIST( 'a', '31-MAY-2022', '42')
);
END;
/
但是,这些值现在都作为字符串传递到动态 SQL 语句中,这使得将数据传递到 DATE
、TIMESTAMP
或 INTERVAL
变得更加困难(等)列,因为它依赖于隐式 data-type 转换。
您仍应尽可能避免动态 SQL。
db<>fiddle here
我需要编写一个程序,其中将 table 名称作为输入参数(一次应该只有一个 table)和列名(应该有多个列名逗号分隔)和列值(应该有多个列名逗号分隔)。
我的尝试:
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN VARCHAR2,-- It can have multiple column names separated by comma
p_column_value IN VARCHAR2-- It can have multiple column names separated by
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '||p_table_name||'should have multiple column names' ||
'VALUES('||'should have multiple column names' ||')';
EXECUTE IMMEDIATE lv_str;
END;
Tool used: SQL Developer(18c)
我对如何在过程主体内处理多个列名和列值感到困惑。我将如何定义一个数组并相应地进行?
不要。您正在为自己设置易受 SQL 注入攻击的程序。
如果你真的想要(请不要):
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN VARCHAR2,
p_column_value IN VARCHAR2
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '||p_table_name||' (' || p_column_name || ') VALUES(' || p_column_value ||')';
EXECUTE IMMEDIATE lv_str;
END;
/
那么你可以这样做:
BEGIN
sp_test_insert(
'my_table',
'col1, col2, col3',
q'['a', DATE '2022-05-31', 42]'
);
END;
/
但你也可以这样做:
BEGIN
sp_test_insert(
'my_table',
'col1, col2, col3',
q'['a', (SELECT DATE '1970-01-01' FROM secret_table WHERE username = 'Admin' AND password_hash = 'abcgefg1234'), 42]'
);
END;
/
不要让您的应用程序容易受到 SQL 注入攻击;尽量避免动态 SQL!
如果你想让它更能抵抗 SQL 注入攻击,那么你可以使用 DBMS_ASSERT
包:
CREATE OR REPLACE PROCEDURE sp_test_insert(
p_table_name IN VARCHAR2,
p_column_name IN SYS.ODCIVARCHAR2LIST,
p_column_value IN SYS.ODCIVARCHAR2LIST
)
AS
lv_str VARCHAR2(4000);
BEGIN
lv_str := 'INSERT INTO '
|| DBMS_ASSERT.SQL_OBJECT_NAME(
DBMS_ASSERT.ENQUOTE_NAME(p_table_name, FALSE)
)
||' ('
|| DBMS_ASSERT.ENQUOTE_NAME(p_column_name(1), FALSE);
FOR i IN 2 .. p_column_name.COUNT LOOP
lv_str := lv_str || ', '
|| DBMS_ASSERT.ENQUOTE_NAME(p_column_name(i), FALSE);
END LOOP;
lv_str := lv_str || ') VALUES('
|| DBMS_ASSERT.ENQUOTE_LITERAL(p_column_value(1));
FOR i IN 2 .. p_column_name.COUNT LOOP
lv_str := lv_str || ', '
|| DBMS_ASSERT.ENQUOTE_LITERAL(p_column_value(i));
END LOOP;
lv_str := lv_str || ')';
EXECUTE IMMEDIATE lv_str;
END;
/
然后:
BEGIN
sp_test_insert(
'MY_TABLE',
SYS.ODCIVARCHAR2LIST( 'COL1', 'COL2', 'COL3'),
SYS.ODCIVARCHAR2LIST( 'a', '31-MAY-2022', '42')
);
END;
/
但是,这些值现在都作为字符串传递到动态 SQL 语句中,这使得将数据传递到 DATE
、TIMESTAMP
或 INTERVAL
变得更加困难(等)列,因为它依赖于隐式 data-type 转换。
您仍应尽可能避免动态 SQL。
db<>fiddle here