云计算百科
云计算领域专业知识百科平台

【GaussDB】507内核新特性-PLSQL字节码(三)

本文根据日志描述反向倒推测试场景,尝试命中更多上一篇没有覆盖到的BC日志码。

测试环境与上一篇一致:GaussDB 507.0.0 (build 19fa72ae),A兼容模式,plsql_code_type=BYTECODE。

由于没有gsql客户端,本次测试使用Java + gaussdbjdbc.jar连接数据库,通过JDBC的SQLWarning链捕获client_min_messages=log输出的LOG消息,效果与gsql终端输出一致。

// 核心思路:JDBC的SQLWarning可以捕获server端LOG消息

Statement st = conn.createStatement();

st.execute("set plsql_code_type=BYTECODE");

st.execute("set client_min_messages=log");

st.execute("set logging_module='on(BYTECODE)'");

st.execute("call some_proc()");

SQLWarning w = st.getWarnings();

while (w != null) {

    System.out.println(w.getMessage());

    w = w.getNextWarning();

}

BC053: bytecode only support select statement.

上一篇只在TRIGGER场景命中过BC053,实际上只要是存储过程里直接写DML语句(INSERT/UPDATE/DELETE/MERGE),都会触发这个日志。

create or replace procedure p_bc_insert is

begin

  insert into dml_test values(1,1);

end;

/

LOG:  [ByteCode] BC053 bytecode only support select statement.

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33663.

LOG:  [ByteCode] Function: p_bc_insert(), InterpreterBytecode:

[0] exec_stmt_begin 

[21] exec_stmt_treebased  

[34] exec_stmt_end 

[47] exec_stmt_begin 

[68] exec_stmt_end 

[81] return0 5 

LOG:  Start ByteCode!

UPDATE、DELETE、MERGE同理,都输出BC053,但存储过程本身仍然支持字节码,只是DML语句编译为exec_stmt_treebased指令。exec_stmt_treebased是字节码指令集中的一个正常指令,字节码引擎执行到该指令时会调用AST引擎处理这一条语句,然后继续执行下一条字节码指令。也就是说,存储过程里只要有DML,那部分就是树引擎跑的,其余部分还是字节码。

FORALL语句也是如此,不管FORALL里是UPDATE还是INSERT还是DELETE,都会输出BC053:

create or replace procedure p3_forall_upd is

type num_arr is varray(10) of int;

arr num_arr := num_arr(1,2,3);

begin

forall i in 1..arr.count

update forall_test3 set val = val + 100 where id = arr(i);

end;

/

LOG:  [ByteCode] BC053 bytecode only support select statement.

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33724.

FORALL本身是支持字节码的(fori_prepare + fori),只是循环体里的DML语句编译为exec_stmt_treebased。但FORALL带SAVE EXCEPTIONS则会直接导致整个存储过程不支持字节码(BC027 + BC030 + BC039),上一篇已测过。

BC054: Simple expr check occurs exeception. Failed to eval simple expr.

这个日志在异常处理块里被触发了,具体场景是过程体里有一个会在编译期求值失败的表达式:

create or replace procedure p_bc_except is

v int;

begin

  v := 1/0;

exception

when others then

  v := 0;

end;

/

LOG:  [ByteCode] BC054 Simple expr check occurs exeception. Failed to eval simple expr.

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33651.

LOG:  [ByteCode] Function: p_bc_except(), InterpreterBytecode:

[0] exec_stmt_begin 

[21] exec_block_init 

[22] exec_block_begin 

[27] exec_stmt_begin 

[48] exec_stmt_treebased  

[61] exec_stmt_end 

[74] exec_block_end 

LOG:  Start ByteCode!

1/0在简单表达式检查时抛出了除零异常,导致该语句编译为exec_stmt_treebased指令,但整个存储过程仍支持字节码。BC054的含义应该是:字节码引擎在尝试将表达式编译为字节码指令时,如果简单表达式求值检查失败(比如常量折叠时除零),就会将该语句改为exec_stmt_treebased,交给树引擎执行。

BC055 + BC072: 带出参函数在简单表达式中调用

当存储过程里通过SELECT语句调用一个带OUT参数的函数时,会触发BC055:

create or replace function f2_bc061(o out int) return int is

begin

  o := 1;

  return 2;

end;

/

create or replace procedure p2_bc061 is

v int;

o int;

begin

  select f2_bc061(o) into v;

end;

/

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33675.

LOG:  [ByteCode] Function: p2_bc061(), InterpreterBytecode:

[0] exec_stmt_begin 

[21] expression expr: select f2_bc061(o) 

[58] storen_sql_attributes 

[59] storen 9 

[64] exec_stmt_end 

LOG:  Start ByteCode!

LOG:  [ByteCode] BC055 function(33674) with outparam called in simple expr does not support.

LOG:  [ByteCode] BC072 Expression: select f2_bc061(o) is degenerated.

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33674.

BC055表示带出参的函数在简单表达式中调用时不支持字节码,BC072表示该表达式被"退化"(degenerated)。调用方存储过程仍然支持字节码,被调用的带出参函数本身也支持字节码,只是调用点这个表达式编译为exec_stmt_treebased

BC061: 带出参函数在PERFORM语句中使用

create or replace procedure p2_bc064 is

o int;

begin

  perform f2_bc064(o);

end;

/

LOG:  [ByteCode] BC061 not spport func with out param used in execsql statement, sql is :perform f2_bc064(o);

LOG:  [ByteCode] BC040 This function supports bytecode, func_oid: 33679.

BC061表示在execsql语句(PERFORM也是一种execsql)中使用了带出参的函数,不支持字节码,但存储过程本身仍支持。注意这里BC061的日志文本里有个拼写错误:"spport"应该是"support"。

BC074: behavior_compat_flags 检查

这个日志在关闭proc_outparam_override后创建带出参的函数时触发:

— 先关闭 proc_outparam_override

set behavior_compat_options='…(不包含proc_outparam_override)…';

create or replace function f3_bc056(o out int) return int is

begin

  o := 1;

  return 2;

end;

/

declare o int; v int; begin v:=f3_bc056(o); end;

LOG:  [ByteCode] BC074 This function can not supports bytecode, func_oid: 33704.Please check the GUC parameter behavior_compat_flags.

select bytecode from dump_plsql_bytecode('f3_bc056'::regproc);

  Not supported

BC074直接导致函数不支持字节码。这个日志对应文档约束中"该特性仅在兼容性参数behavior_compat_options设置中包含…proc_outparam_override…参数选项时,支持执行字节码"。实际测试发现,不是某个单独的参数控制,而是只要proc_outparam_override不在behavior_compat_options里,带出参的函数就会触发BC074。普通存储过程(不带出参)在关闭proc_outparam_override时仍然支持字节码。

BC030: GOTO语句不支持

GOTO语句会导致整个存储过程不支持字节码:

create or replace procedure p3_goto_valid is

v int := 0;

begin

  v := 1;

  goto skip;

  v := 2;

  <<skip>>

  v := 3;

end;

/

LOG:  [ByteCode] BC030 Stmt GOTO does not support with some error, lineno: 5.

LOG:  [ByteCode] BC039 This function doesn't support bytecode, func_oid: 33720.

不管GOTO的标签是否有效,只要存储过程里用了GOTO,整个存储过程就不支持字节码。而EXIT和CONTINUE(带标签或不带标签)都是支持字节码的。

BC068: subtype的新触发场景

上一篇测BC068时,只在嵌套子程序场景命中过。这次发现subtype变量也能触发BC068,而且会直接导致整个存储过程不支持字节码。

场景1:subtype定义在同一个package中

create or replace package pkg_bc036b is

subtype myint is int;

procedure p;

end;

/

create or replace package body pkg_bc036b is

procedure p is

v myint;

begin

  v := 1;

  v := v + 1;

  raise notice '%', v;

end;

end;

/

LOG:  [ByteCode] BC068 bytecode neither support nest sub procedure/function nor subtype.

select bytecode from dump_plsql_bytecode('pkg_bc036b.p'::regproc);

  Not supported

场景2:subtype定义在匿名块中

create or replace procedure p_bc_subtype2 is

begin

  declare

    subtype myint is int;

    v myint;

  begin

    v := 1;

  end;

end;

/

LOG:  [ByteCode] BC068 bytecode neither support nest sub procedure/function nor subtype.

select bytecode from dump_plsql_bytecode('p_bc_subtype2'::regproc);

  Not supported

但在上一篇中测试的跨package引用subtype(v pkg5.varchar8),却只触发了BC036且仍然支持字节码。这说明subtype的不支持行为存在不一致:同package内定义的subtype或匿名块内的subtype会导致BC068(整体不支持),而跨package引用subtype只触发BC036(局部不支持,整体仍支持)。考虑到BC068的日志文本是"bytecode neither support nest sub procedure/function nor subtype",subtype和嵌套子程序共用同一个检查逻辑,这恐怕是个实现上的简化处理。

确认支持字节码的场景

以下场景在测试中确认支持字节码(输出BC040 + 正常字节码),其中部分语句编译为exec_stmt_treebased(由AST引擎执行该条语句),但存储过程整体仍走字节码框架:

场景

字节码情况

备注

ARRAY构造 array[1,2,3]

支持

storen_array

ROW构造 row(1,2)

支持

eval_rowexpr

 + store_row

子查询赋值 v := (select 1)

treebased

但整体支持

IS NULL表达式

支持

nulltest

CASE WHEN表达式

支持

常量折叠

COALESCE

支持

常量折叠

NULLIF

支持

nullif

GREATEST/LEAST

支持

max

/min

XML类型操作

支持

xml XMLPARSE

数组元素赋值 v(1) := 10

支持

store_array_elem

index-by表元素赋值

支持

store_tableofindex_elem

varray元素赋值

支持

同上

GET DIAGNOSTICS

支持

exec_stmt_getdiag

COMMIT/ROLLBACK

treebased

exec_stmt_treebased_transaction

EXECUTE IMMEDIATE

treebased

动态SQL

异常处理 EXCEPTION

支持

exec_exception_begin/end

嵌套匿名块

支持

exec_block_init/begin/end

嵌套RECORD类型

支持

cursor FOR循环

支持

forc_prepare

 + forc

WHILE循环

支持

loopwhile

FOR IN REVERSE循环

支持

fori_prepare_reverse

 + fori_reverse

CONTINUE语句

支持

EXIT/EXIT label

支持

cursor属性 c%rowcount

支持

loadn_cursor_attributes

函数内调用其他函数

支持

invokemethod_definer

带出参函数直接赋值 v := f(o)

支持

procedure expression

REFCURSOR出参

支持

loadn_return_cursor

varray出参

支持

复合类型出参

支持

表类型出参

支持

无法触发的BC日志码

以下BC日志码在本次测试中未能通过SQL语句触发,原因各不相同:

BC码

原因分析

BC001

只有非A兼容模式才会触发,当前环境为A模式

BC002

package初始化匿名块本身不作为字节码对象,内部procedure仍支持

BC008

无法通过正常DDL创建含"无效参数类型"的函数

BC016

processStatement异常,内部错误

BC017/BC018

内部错误,找不到proc或language tuple

BC020

所有测试的表达式类型均支持字节码

BC022

$1参数在字节码中被正常处理为loadn 0

BC023

访问null record的字段正常支持

BC024/BC025

无法构造出"incorrect type"或"invalid op"的场景

BC028

返回复合类型/refcursor均支持

BC029

所有测试的语句类型均支持或treebased

BC031

无权在pg_catalog创建对象

BC032

表类型/复合类型出参均支持

BC033

GET DIAGNOSTICS对record变量字段赋值正常支持

BC034

RESULT_OID诊断类型正常支持

BC037

tableof类型转text正常支持

BC038

bytea类型转换正常支持

BC041/BC042/BC044

数组下标访问正常支持

BC043

无效字段名在运行时报错,不触发BC日志

BC045

XML类型操作正常支持

BC046

集合类型操作正常支持

BC047/BC049

cursor/record赋值均正常支持

BC048

无法创建含无效typeoid的对象

BC052

子查询作为表达式编译为exec_stmt_treebased,但非BC052

BC056

关闭proc_outparam_override时触发的是BC074而非BC056

BC058

函数无返回值时运行报错,dump显示Not supported但无BC日志

BC060

procedure带RETURN值时编译报错,无法执行

BC064

调用方不提供out参数时函数找不到

BC065

出参类型转换正常支持

BC067

系统函数调用时被当作普通函数调用,不影响调用方

BC071/BC073

表达式类型变更/复杂化未触发

BC075

变量数不匹配的EXECUTE INTO正常treebased

BC076

processExpr异常,内部错误

BC078

栈大小异常,内部错误

BC079

权限回收后函数仍显示支持字节码

exit/continue label not match

标签不匹配的procedure编译为invalid

outparam only supports var & row type

refcursor/varray出参均正常支持

其中BC056和BC079值得说明。BC056的日志文本是"bytecode doesn't support function with outparam when proc_outparam_override is off",但实际关闭proc_outparam_override后触发的是BC074("Please check the GUC parameter behavior_compat_flags"),可能是BC056在代码中被BC074覆盖了,或者BC056在更早的版本中使用,507版本改为BC074。BC079的日志文本是"func doesn't support bytecode because of permission error",但revoke execute权限后,函数仍然在第一次执行时生成了字节码(因为函数owner和执行者是同一用户,revoke不影响owner的权限)。

更新后的BC日志码汇总表

在上一篇表格基础上,补充本次新命中的BC日志码:

日志

是否约束

对应文档

是否有命中日志

自测是否支持字节码

备注

BC053 bytecode only support select statement.

Y

Y(本次新增)

Y(局部treebased)

DML语句(INSERT/UPDATE/DELETE/MERGE)和TRIGGER均触发,语句编译为exec_stmt_treebased

BC054 Simple expr check occurs exeception.

Y

Y(本次新增)

Y(局部treebased)

简单表达式求值检查异常,如1/0,语句编译为exec_stmt_treebased

BC055 function(%u) with outparam called in simple expr does not support.

Y

Y(本次新增)

Y(局部treebased)

带出参函数在SELECT表达式中调用,语句编译为exec_stmt_treebased

BC061 not spport func with out param used in execsql statement

Y

Y(本次新增)

Y(局部treebased)

带出参函数在PERFORM语句中使用,日志有拼写错误"spport"

BC074 This function can not supports bytecode…Please check behavior_compat_flags.

Y

兼容性参数要求

Y(本次新增)

N

proc_outparam_override关闭时,带出参函数整体不支持

BC030 Stmt GOTO does not support with some error

Y

Y(本次新增)

N

GOTO语句导致整个存储过程不支持字节码

BC068 …nor subtype.

Y

不支持子程序

Y(已命中+本次新触发)

N

同package内定义的subtype或匿名块内subtype也会触发

BC077 bulk collect does not support.

Y

不支持BULK COLLECT

Y(已命中)

Y(局部treebased)

SELECT BULK COLLECT和FETCH BULK COLLECT均触发,语句编译为exec_stmt_treebased

总结

本次测试新命中了6个之前未覆盖的BC日志码(BC053、BC054、BC055、BC061、BC074、BC030),并发现了BC068的新触发场景(同package subtype)。加上上一篇已命中的,目前二进制中grep出的约50条BC日志码中,已命中约20条。

未能命中的BC日志码大致可分为三类:

1. 内部错误类(BC016/BC017/BC018/BC076/BC078):属于代码内部的异常处理路径,正常SQL无法触发

2. 编译报错类(BC058/BC060/exit label):代码本身就不合规,创建时或执行时就报错了,字节码引擎来不及处理

3. 实际支持类(BC020/BC022/BC023/BC028/BC037/BC038/BC041~BC049/BC052等):测试了各种场景都正常支持字节码,可能这些日志只在特定内部条件下触发,或者对应的功能在507版本已经支持但日志保留了下来

一个比较有意思的发现是,很多不支持场景并不是"全有或全无"的,而是"局部treebased"——不支持的语句编译为exec_stmt_treebased指令(字节码引擎执行到该指令时调用AST引擎处理这一条语句),存储过程整体仍然走字节码框架。真正导致整个存储过程不支持字节码的只有以下场景:

• BC003: TRIGGER

• BC004: 返回集合的函数(returns setof/table)

• BC007: 参数默认值为PACKAGE变量

• BC027 + BC030: FORALL with SAVE EXCEPTIONS

• BC039: (汇总日志,表示该函数不支持)

• BC057: 带出参的FUNCTION返回复合类型

• BC066: 对入参进行赋值

• BC068: 嵌套子程序或同package/匿名块内subtype

• BC070: 伪类型参数

• BC074: behavior_compat_options不满足要求

• BC030: GOTO语句

换句话说,只要避开以上场景,存储过程基本都能走字节码框架,即使内部有些语句以treebased方式执行,整体性能仍然会有提升。

转载:公众号DarkAthena

赞(0)
未经允许不得转载:网硕互联帮助中心 » 【GaussDB】507内核新特性-PLSQL字节码(三)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!