SQLScript 代码分析器由两个内置过程组成,用于扫描 CREATE FUNCTION 和 CREATE PROCEDURE 语句,并搜索指示代码质量、安全性或性能问题的模式。
列出可用规则的视图 SQLSCRIPT_ANALYZER_RULES 通过以下方式定义: 列名称类型RULE_NAMESPACEVARCHAR(16)RULE_NAMEVARCHAR(64)类别VARCHAR(16)SHORT_DESCRIPTIONVARCHAR(256)LONG_DESCRIPTIONNVARCHAR(5000)建议NVARCHAR(5000)
ANALYZE_SQLSCRIPT_DEFINITION 过程分析尚未创建的单个过程或函数的源代码。如果过程或函数参考不存在的对象,则无法执行分析。
示例代码CREATE PROCEDURE ANALYZE_SQLSCRIPT_DEFINITION( IN OBJECT_DEFINITION NCLOB, IN RULES TABLE( RULE_NAMESPACE VARCHAR(16), RULE_NAME VARCHAR(64), CATEGORY VARCHAR(16) ), OUT FINDINGS TABLE( RULE_NAMESPACE VARCHAR(16), RULE_NAME VARCHAR(64), CATEGORY VARCHAR(16), SHORT_DESCRIPTION NVARCHAR(256), START_POSITION INT, END_POSITION INT )) AS BUILTIN;
参数描述OBJECT_DEFINITION包含要分析的 SQLScript 函数或过程的数据定义语言 (DDL) 字符串。规则要用于分析的规则。可以从视图 SQLSCRIPT_ANALYZER_RULES 中检索可用规则。FINDINGS列出分析期间发现的潜在问题
用户创建过程或函数时,可能会出现潜在问题。通过使用 DDL 语句执行 ANALYZE_SQLSCRIPT_DEFINITION 过程,将生成一个结果集,其中包括检测到的问题列表(包括警告和错误)以及任何规则应用程序结果。
现在,可以在 SQLScript 代码分析器实际创建之前检测执行 DDL 语句期间出现的警告消息。
以下代码示例演示了在使用 ANALYZE_SQLSCRIPT_DEFINITION 过程创建潜在问题之前如何分析 SQLScript 函数是否存在潜在问题:
DO BEGIN
RULES = SELECT RULE_NAMESPACE, RULE_NAME, CATEGORY FROM SQLSCRIPT_ANALYZER_RULES;
CALL ANALYZE_SQLSCRIPT_DEFINITION('
CREATE FUNCTION sudf_select_into()
RETURNS val INT
AS BEGIN
SELECT 123 INTO val FROM dummy;
END;', :RULES, RES);
SELECT * FROM :RES;
END;
('INTERNAL', None, 'WARNING', 'Not recommended feature: Using SELECT INTO in Scalar UDF', 72, None)
示例结果:
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITION内部没有警告不推荐的功能:在标量用户定义函数 (UDF) 中使用 SELECT INTO72没有
检测到警告时,将与其他应用规则的结果(如果有)一起显示。
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITION内部没有警告<警告消息>位置Position 或 null(如果不存在)SAP<规则名称><规则类别><规则描述>位置位置
如果在 SQLScript 编译期间发生错误,将报告如下:
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITION内部没有ERROR<错误消息>位置Position 或 null(如果不存在)
SAP 目前提供的以下规则可用:
列:2 已选择全部清除 规则名称类别 规则名称类别UNNECESSARY_VARIABLE一致性UNUSED_VARIABLE_VALUE一致性UNCHECKED_SQL_INJECTION_SAFETY安全SINGLE_SPACE_LITERAL一致性COMMIT_OR_ROLLBACK_IN_DYNAMIC_SQL样式USE_OF_SELECT_IN_SCALAR_UDF性能USE_OF_UNASSIGNED_SCALAR_VARIABLE一致性DML_STATEMENTS_IN_LOOPS性能USE_OF_CE_FUNCTIONS性能USE_OF_DYNAMIC_SQL性能ROW_COUNT_AFTER_SELECT行为ROW_COUNT_AFTER_DYNAMIC_SQL行为USE_OF_SET_SESSION_VARIABLE信息USE_OF_DDL_STATEMENT信息USE_OF_WITH_HINT_CLAUSE信息OUTPUT_LENGTH_VIOLATION一致性 页面大小:10显示 1–10 条,共 16 条12 UNNECESSARY_VARIABLE
对于每个变量,都会测试它是否由过程的任何输出参数使用,或者是否影响过程的结果。与结果相关的语句可以是 DML 语句、隐式结果集、控制语句条件。
UNUSED_VARIABLE_VALUE
如果赋给变量的值未在任何其它语句中使用,则可以删除赋值。如果是 DECLARE 语句中的缺省分配,则永远不会使用缺省值。
UNCHECKED_SQL_INJECTION_SAFETY
如果在动态 SQL 中使用 SQL 注入安全,应始终检查字符串类型的参数。此规则检查是否为该类型的每个参数调用函数 is_sql_injection_safe。
对于 IF is_sql_injection_safe(:var) = 0 THEN... 之类的简单条件语句,检查真分支中的控制流。该过程应结束(通过返回或通过抛出错误),或者应使用函数 escape_single_quotes 或 escape_double_quotes 转义不安全的参数值,具体取决于值的使用位置。
如果条件更复杂(例如,在一个条件中检查多个变量),将显示警告,因为只能检查动态 SQL 的任何执行是否通过 SQL 注入检查。
SINGLE_SPACE_LITERAL
此规则搜索仅包含一个空格的字符串 aterals。如果使用 ABAP VARCHAR MODE,则将此类字符串文字视为空字符串。在这种情况下,可使用 CHAR(32) 代替 ' '。
COMMIT_OR_ROLLBACK_IN_DYNAMIC_SQL
此规则检测使用 COMMIT 或 ROLLBACK 语句的动态 SQL。建议直接在 SQLScript 中使用 COMMIT 和 ROLLBACK,从而消除动态 SQL 的需求。
此规则在分析动态 SQL 方面存在一些限制:
COMMIT';)。它无法检测计算任何表达式的动态 SQL(例如 EXEC 'COM' || 'MIT';)
COMMIT 或 ROLLBACK 和空格的简单字符串以及简单注释。此规则可能不会检测到更复杂的字符串。
USE_OF_SELECT_IN_SCALAR_UDF
此规则检测并报告标量 UDF 中的 SELECT 语句。标量 UDF 中的 SELECT 语句会影响性能。如果确实需要表操作,则应改用过程或表 UDF。
示例代码USE_OF_SELECT_IN SCALAR_UDFDO BEGIN tab = SELECT RULE_NAMESPACE, RULE_NAME, category FROM SQLSCRIPT_ANALYZER_RULES where rule_name = 'USE_OF_SELECT_IN_SCALAR_UDF'; CALL ANALYZE_SQLSCRIPT_DEFINITION(' CREATE FUNCTION f1(a INT) RETURNS b INT AS BEGIN DECLARE x INT; SELECT count(*) into x FROM _sys_repo.active_object; IF :a > :x THEN SELECT count(*) INTO b FROM _sys_repo.inactive_object; ELSE b = 100; END IF; END;', :tab, res); SELECT * FROM :res;END; 在此示例中,将报告以下查找结果: RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITIONSAPUSE_OF_SELECT_IN_SCALAR_UDF性能在标量 UDF 中找到 SELECT 语句186240SAPUSE_OF_SELECT_IN_SCALAR_UDF性能在标量 UDF 中找到 SELECT 语句97149 USE_OF_UNASSIGNED_SCALAR_VARIABLE
该规则检测已使用但从未明确分配的变量。这些变量在使用时仍具有默认值,可能未定义。建议分配一个默认值(可以是 NULL),以确保从变量中读取时获得所需的值。如果此规则返回警告或错误,请检查代码中是否没有为错误的变量赋值。更改代码后始终重新运行此规则,因为可能多个错误仅触发一条消息,并且错误仍然存在。
对于每个 DECLARE 语句,此规则返回以下内容之一:
DML_STATEMENTS_IN_LOOPS
该规则在循环中检测以下 DML 语句 - INSERT、UPDATE、DELETE、REPLACE/UPSERT。有时,可以重写循环并使用单个 DML 语句来提高性能。
在以下示例中,表在 循环中更新。可以重写此代码以使用单个 DML 语句更新表。
示例代码循环中的 DML 语句DO BEGINtab = select rule_namespace, rule_name, category from sqlscript_analyzer_rules;call analyze_sqlscript_definition(' Create procedure example() ASBEGIN declare i int = 0; declare size int; declare olda int; declare newa int; CREATE TABLE T1 (a INT); INSERT INTO T1 VALUES(1); INSERT INTO T1 VALUES(-2); INSERT INTO T1 VALUES(-1); INSERT INTO T1 VALUES(3); T2 = SELECT * FROM T1; SELECT COUNT(*) INTO size FROM T1; FOR i IN 1 .. :size DO olda = :T2.A[:i]; newa = :olda; if :olda < 0 then newa = 0; end if; UPDATE T1 SET A= :newa WHERE A = :olda; END FOR; SELECT * FROM T1;END; ', :tab, res);select * from :res;end; // Optimized version drop procedure example2;Create procedure example2() ASBEGIN declare i int = 0; declare size int; declare olda int; declare newa int; CREATE TABLE T1 (a INT); INSERT INTO T1 VALUES(1); INSERT INTO T1 VALUES(-2); INSERT INTO T1 VALUES(-1); INSERT INTO T1 VALUES(3); UPDATE T1 SET A = 0 WHERE A < 0; SELECT * FROM T1;END; DROP TABLE T1;CALL EXAMPLE2(); USE_OF_CE_FUNCTIONS
该规则检查是否使用计算引擎计划运算符(CE 函数)。由于它们使优化更加困难并导致性能问题,因此应避免这种情况。有关详细信息以及如何仅使用纯 SQL 替换它们,请参阅计算引擎计划运算符
USE_OF_DYNAMIC_SQL
规则检查并报告是否在过程或函数中使用动态 SQL。
ROW_COUNT_AFTER_SELECT
该规则检查系统变量 ::ROWCOUNT 是否在 SELECT 语句之后使用。
ROW_COUNT_AFTER_DYNAMIC_SQL
该规则检查在使用动态 SQL 后是否使用系统变量 ::ROWCOUNT。
USE_OF_SET_SESSION_VARIABLE
此规则标识管理会话变量的 SQL 语句。会话变量用于存储可在整个会话期间访问的信息,从而允许自定义会话行为。常见用例包括设置时区、字符集或其他影响执行环境或查询结果的会话特定设置。正确管理会话变量可以增强应用程序功能和用户体验。
在提供的示例中,应用 'USE_OF_SET_SESSION_VARIABLE' 规则来识别和管理代码中的会话变量。以下生成的输出表汇总了规则的查找结果,详细说明设置和取消设置会话变量的特定位置。
DO BEGIN
tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES;
call sys.analyze_sqlscript_definition('
do begin
set ''session_var'' = ''hana'';
unset ''session_var'';
end;
', :tab, res);
select * from :res;
END;
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITIONSAPUSE_OF_SET_SESSION_VARIABLE信息找到会话变量管理:session_var1339SAPUSE_OF_SET_SESSION_VARIABLE信息找到会话变量管理:session_var4463 USE_OF_DDL_STATEMENT
此规则检测 SQL 脚本中的数据定义语言 (DDL) 语句。DDL 语句涉及定义、修改或删除数据库模式、对象(如表和索引)的命令,从而影响数据库数据的结构。识别这些语句有助于了解数据库中的模式更改和数据管理策略。
DO BEGIN
tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES;
call sys.analyze_sqlscript_definition('
create procedure proc1() as begin
create table ta(a int);
insert into ta values (1);
drop table tab;
end;
', :tab, res);
select * from :res;
END;
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITIONSAPUSE_OF_DDL_STATEMENT信息找到了 DDL 语句的使用92106SAPUSE_OF_DDL_STATEMENT信息找到了 DDL 语句的使用3759 USE_OF_WITH_HINT_CLAUSE
此规则旨在检测 SQL 查询中的 "WITH HINT" 子句。提示是 SQL 中指导数据库引擎如何执行查询(可能覆盖缺省查询执行计划)的特殊说明。其中包括有关索引使用、连接策略或其它优化提示的建议。虽然提示可以提高性能,但需要仔细考虑,因为如果数据库的数据分配随时间变化,它们可能会导致不太理想的执行计划。
在提供的示例中,SQLScript 分析器应用 USE_OF_WITH_HINT_CLAUSE 规则来检查 SQL 过程中 "WITH HINT" 子句的使用。下表说明了过程中检测到的提示,标记它们在脚本中的位置。
DO BEGIN
tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES;
call sys.analyze_sqlscript_definition('
create procedure proc1(out o table(col nvarchar(10))) as begin
o = select * from tudf1() with hint(workload_class("WLC"), no_inline);
end;
', :tab, res);
select * from :res;
END;
RULE_NAMESPACERULE_NAME类别SHORT_DESCRIPTIONSTART_POSITIONEND_POSITIONSAPUSE_OF_WITH_HINT_CLAUSE信息no_inline126135SAPUSE_OF_WITH_HINT_CLAUSE信息workload_class("WLC1")102124 OUTPUT_LENGTH_VIOLATION
此规则执行静态分析,以检测过程及其嵌套过程中的输出长度违规。识别分配到输出表列的数据超出定义的列长度(这可能导致执行期间出现错误)的情况。
为避免此类错误,请确保正确定义了过程或表用户定义函数 (UDF) 的输出表类型或返回表类型。如有必要,调整列长度以适应分配的数据。此规则还评估输入中指定的嵌套过程和表 UDF,以识别潜在问题。
结果按以下格式显示:
""."".""." 示例:
以下示例演示了 OUTPUT_LENGTH_VIOLATION 规则如何识别已分配数据长度与嵌套过程和函数设置中定义的列长度之间的潜在不匹配。
CREATE SCHEMA codeanalyzer;
SET SCHEMA codeanalyzer;
CREATE TABLE tab(col NVARCHAR(5));
INSERT INTO tab VALUES ('abcde');
CREATE PROCEDURE inner_proc(OUT ot1 TABLE(col NVARCHAR(4))) AS
BEGIN
ot1 = SELECT * FROM tab;
END;
CREATE FUNCTION inner_func
RETURNS TABLE(col NVARCHAR(4)) AS
BEGIN
RETURN SELECT * FROM tab;
END;
DO BEGIN
tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES;
CALL sys.analyze_sqlscript_definition('
CREATE OR REPLACE PROCEDURE outer_proc(OUT ot2 TABLE(col NVARCHAR(3))) AS
BEGIN
CALL inner_proc(ot2);
END;', :tab, res);
SELECT * FROM :res;
END;
在此示例中:
该规则标记每个级别定义的长度与实际分配数据长度之间的不匹配。预期输出显示在下表中。
命名空间规则名称类别描述开始位置结束位置SAPOUTPUT_LENGTH_VIOLATION一致性“WM0”。”CODEANALYZER”。”INNER_PROC"."."OT1”。COL”:目标类型 NVARCHAR(4)/已分配类型 NVARCHAR(5)。8696 下一个示例演示当程序将函数的输出分配给其输出表时 OUTPUT_LENGTH_VIOLATION 规则如何标识长度违规:
DO BEGIN
tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES;
call sys.analyze_sqlscript_definition('
create procedure outer_proc(out ot2 table(col nvarchar(3))) as
begin
ot2 = select * from inner_func();
end;', :tab, res);
select * from :res;
END;
在此示例中,出现此问题的原因是:
下表显示预期输出。
命名空间规则名称类别描述开始位置结束位置内部 警告常规警告:表变量 "OT2" 的类型不匹配:属性 "COL" 的目标类型 "NVARCHAR(3)" 与分配类型 "NVARCHAR(4)" 不同:行 4 列 1(位于位置 73)。70 SAPOUTPUT_LENGTH_VIOLATION一致性“WM0”。”CODEANALYZER”。”OUTER_PROC"."."OT2”。COL”:目标类型 NVARCHAR(3)/已分配类型 NVARCHAR(4)。3359SAPOUTPUT_LENGTH_VIOLATION一致性“WM0”。”CODEANALYZER”。”INNER_FUNC"."."(FUNCTION RETURN TABLE)"."COL”:目标类型 NVARCHAR(4)/已分配类型 NVARCHAR(5)。90100
示例代码DO BEGIN tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES; -- selects all rules CALL ANALYZE_SQLSCRIPT_DEFINITION(' CREATE PROCEDURE UNCHECKED_DYNAMIC_SQL(IN query NVARCHAR(500)) AS BEGIN DECLARE query2 NVARCHAR(500) = ''SELECT '' || query || '' from tab''; EXEC :query2; query2 = :query2; --unused variable value END', :tab, res); SELECT * FROM :res;END; 示例代码DO BEGIN tab = SELECT rule_namespace, rule_name, category FROM SQLSCRIPT_ANALYZER_RULES; to_scan = SELECT schema_name, procedure_name object_name, definition FROM sys.procedures WHERE procedure_type = 'SQLSCRIPT2' AND schema_name IN('MY_SCHEMA','OTHER_SCHEMA') ORDER BY procedure_name; CALL analyze_sqlscript_objects(:to_scan, :tab, objects, findings); SELECT t1.schema_name, t1.object_name, t2.*, t1.object_definition FROM :findings t2 JOIN :objects t1 ON t1.object_definition_id = t2.object_definition_id;END;
由于静态代码分析的性质,SQLScript 代码分析器可能会产生误报。为避免在分析具有许多查找结果且可能存在许多误报的大型过程时出现混淆,代码分析器提供了一种手动禁止这些误报的方法。
您可以使用 SQLScript 编译器定义应禁止的规则。编译指示指令名称为 AnalyzerSuppress,并且必须至少有一个参数描述应禁止哪个规则。
示例代码create procedure proc as begin @AnalyzerSuppress('SAP.UNNECESSARY_VARIABLE.CONSISTENCY') declare a int;end