DB2 SQL 错误:执行预准备语句时出现 SQLCODE=-302



我有一个SQL查询,它接受用户输入,因此存在安全漏洞。

现有查询为:

SELECT  BUS_NM, STR_ADDR_1, CITY_NM, STATE_CD, POSTAL_CD, COUNTRY_CD,
BUS_PHONE_NB,PEG_ACCOUNT_ID, GDN_ALERT_ID, GBIN, GDN_MON_REF_NB,
ALERT_DT, ALERT_TYPE, ALERT_DESC,ALERT_PRIORITY 
FROM ( SELECT A.BUS_NM, AE.STR_ADDR_1, A.CITY_NM, A.STATE_CD, A.POSTAL_CD,
CC.COUNTRY_CD,  A.BUS_PHONE_NB, A.PEG_ACCOUNT_ID, 'I' || 
LPAD(INTL_ALERT_DTL_ID, 9,'0') GDN_ALERT_ID, 
LPAD(IA.GBIN, 9,'0') GBIN, IA.GDN_MON_REF_NB,
DATE(IAD.ALERT_TS) ALERT_DT, 
XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing  
IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE, 
, ROW_NUMBER() OVER () AS "RN" 
FROM ACCOUNT A, Other tables 
WHERE IA.GDN_MON_REF_NB = '100'  
AND A.PEG_ACCOUNT_ID = IAAR.PEG_ACCOUNT_ID  
AND CC.COUNTRY_CD = A.COUNTRY_ISO3_CD 
ORDER BY IA.INTL_ALERT_ID ASC )  
WHERE  ALERT_TYPE  IN (" +TriggerType+ ");

我将其更改为接受来自setString的TriggerType,例如:

SELECT BUS_NM, STR_ADDR_1, CITY_NM, STATE_CD, POSTAL_CD, COUNTRY_CD,
BUS_PHONE_NB,PEG_ACCOUNT_ID, GDN_ALERT_ID, GBIN, GDN_MON_REF_NB,
ALERT_DT, ALERT_TYPE, ALERT_DESC,ALERT_PRIORITY 
FROM ( SELECT A.BUS_NM, AE.STR_ADDR_1, A.CITY_NM, A.STATE_CD, A.POSTAL_CD, 
CC.COUNTRY_CD,  A.BUS_PHONE_NB, A.PEG_ACCOUNT_ID, 
'I' || LPAD(INTL_ALERT_DTL_ID, 9,'0') GDN_ALERT_ID,
LPAD(IA.GBIN, 9,'0') GBIN, IA.GDN_MON_REF_NB, 
DATE(IAD.ALERT_TS) ALERT_DT, 
XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing  
IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE, 
ROW_NUMBER() OVER () AS "RN" 
FROM ACCOUNT A, other tables 
WHERE IA.GDN_MON_REF_NB = '100'  
AND A.PEG_ACCOUNT_ID = IAAR.PEG_ACCOUNT_ID   
AND CC.COUNTRY_CD = A.COUNTRY_ISO3_CD 
ORDER BY IA.INTL_ALERT_ID ASC )  
WHERE  ALERT_TYPE  IN (?);

设置触发器类型如下:

if (StringUtils.isNotBlank(request.getTriggerType())) {
preparedStatement.setString(1, triggerType != null ? triggerType.toString() : "");
}

获取错误为

由以下原因引起:com.ibm.db2.jcc.am.SqlDataException: DB2 SQL 错误: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.19.26

参数的IN谓词用法错误。
不要期望IN ('AAAA, M250, ABCD')(当您尝试将逗号分隔的字符串作为单个参数传递时)可以IN ('AAAA', 'M250', 'ABCD')工作(根据需要)。这些谓词不等效。
如果你想传递这样一个逗号分隔的字符串,你需要一些"字符串分词器",如下所示。

select t.*
from
(
select XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE
from table(values xmlparse(document '<alertTypeConfig><biqCode>M250, really big code</biqCode></alertTypeConfig>')) IAC(INTL_ALERT_TYPE_CONFIG)
) t
--WHERE  ALERT_TYPE IN ('AAAA, M250, ABCD')
join xmltable('for $id in tokenize($s, ",s?") return <i>{string($id)}</i>' 
passing cast('AAA, M250 , ABCD' as varchar(200)) as "s" 
columns token varchar(200) path '.') x on x.token=t.ALERT_TYPE
;

按原样运行语句。然后,您可以使用WHERE子句取消注释字符串,并注释掉其余部分以查看您尝试执行的操作。

PS:
您得到的错误可能是因为您没有指定参数的数据类型(您不使用类似IN (cast(? as varchar(xxx)))的东西,并且 db2 编译器假定其长度等于ALERT_TYPE表达式的长度(4 个字节)。

-302 SQLCODE 表示某种转换错误。

SQLSTATE 22001 通过告诉我们您正在尝试将大字符串强制为小变量来缩小范围。鉴于您问题中的信息有限,我猜罪魁祸首是XMLCAST

DB2 不会把 30 磅的垃圾塞进一个 4 磅重的袋子里,可以这么说,它会给你一个错误。也许在演员阵容中给XML一些额外的空间可能会有所帮助。如果您需要确保它最终只有 4 个字符长,您可以显式执行LEFT(XMLCAST( ... AS VARCHAR(64)), 4).这样,XMLCAST就有了它需要的空间,但你把它剪掉了,以适应你的变量在抓取时。

另一件事可能是传递给参数标记的变量太长。DB2 将根据ALERT_TYPE的长度猜测类型和长度。请注意,只能通过参数标记传递单个值。如果传递逗号分隔列表,它将不会按预期运行(除非您希望ALERT_TYPE也包含逗号分隔列表)。如果要从表中获取逗号分隔的列表,则可以改用子选择。