在 Oracle PLSQL 中将动态字符串分配给 varchar2/clob 变量时发生错误(没有更多数据可从套接字读取)

Error occured (No more data to read from socket) while assigning a dynamic string to varchar2/clob variable in Oracle PLSQL

提问人:ZeRoOo 提问时间:11/3/2023 最后编辑:MT0ZeRoOo 更新时间:11/5/2023 访问量:122

问:

我通过连接多个参数/值并分配给包中定义的局部变量来创建动态字符串。一旦我编译了包连接,就会出现错误,指出“没有更多数据可从套接字读取”。

由于这是连接断开的一般错误,但在编译包时也是如此,这让我想知道查看代码,我发现当使用多个串联的动态字符串被分配给 clob 变量时,就会发生这种情况。

我已经在一个匿名块中简化并复制了该场景。

当连接字符串长度超过 2277 个字符时,它会导致错误,而变量使用 varchar2(4000 char) 声明。如果我删除串联并简单地传递字符串,它就可以按照声明的限制(在这种情况下最多 4000 个字符)正常工作。

我正在使用 Oracle Database 19c EE。

与串联(导致错误):

select length(
'1'||'2'||'3'||'4'||'5'||'6'||'7'||'8'||'9'||'0'||
-- (removed the part, it is copy paste of above line until characters length reaches 2278)
'1'||'2'||'3'||'4'||'5'||'6'||'7'||'8'
) len_concat_string from dual;

执行的输出截图1

declare
v_sql varchar2(4000 char);
begin
v_sql :=
'1'||'2'||'3'||'4'||'5'||'6'||'7'||'8'||'9'||'0'||
'1'||'2'||'3'||'4'||'5'||'6'||'7'||'8'||'9'||'0'||
-- (removed the part, it is copy paste of above line until characters length reaches 2278)
'1'||'2'||'3'||'4'||'5'||'6'||'7'||'8'
;
end;
/

执行的输出截图2

此外,如果我删除一个字符(长度不超过 2277),那么它就会按预期工作。

执行输出截图3

无串联(按预期工作):

select length(
'1234567890
-- (removed the part, it is copy paste of 1234567890 until characters length reaches 4000)
01234567890'
) len_string from dual;

执行输出截图4

declare
v_sql varchar2(4000 char);
begin
v_sql :=
'1234567890
-- (removed the part, it is copy paste of 1234567890 until characters length reaches 4000)
01234567890'
;
end;
/

执行输出截图5

Oracle 数据库版本:

执行输出截图6

SQL PLSQL 预言机19c

评论

1赞 Alex Poole 11/3/2023
查看数据库服务器上的警报日志(如果您没有访问权限,请询问 DBA)以查看报告的错误。据推测,这是一个错误,但真正的潜在错误将允许您通过 Oracle 技术支持进行检查。
0赞 MT0 11/3/2023
无法复制 Oracle 18.0.0.0Oracle 21.0.0.0 中的错误
0赞 MT0 11/3/2023
请不要使用图像(尤其是隐藏在链接后面的图像)来显示输出或错误,因为并非所有用户都可以看到图像。而是复制/粘贴输出并将其作为文本包含(使用 Markdown 格式化)。
1赞 Paul W 11/3/2023
“不再从套接字读取数据”意味着 TCP 连接连接到的影子进程突然终止,甚至不允许在下次调用时向客户端发送错误消息。几乎可以肯定的是,在您的情况下,这是由 ORA-0600(内部错误)引起的,您应该在警报日志和跟踪文件以及可能的事件文件中找到它。找到它后,您可以使用 MOS 或使用他们的 ORA-600 工具进行查找。但我发现,在找到避免错误的解决方法之前,调整代码通常会更快。
1赞 Paul W 11/3/2023
为什么需要在单个作业中执行如此多的文字连接?我敢肯定,你可以以一种避免你遇到的任何错误的方式编写它,只需不使用像这样突破极限的极端模式进行编码。如果需要将 SQL 组装成小块,请在循环中以编程方式逐步构建变量,而不是一次使用数千个运算符。当我们做别人没有做的事情时,错误很普遍,因为这些是 Oracle 测试过的最不可能的场景。||

答:

1赞 Jon Heller 11/5/2023 #1

我相信您的过度连接代码遇到了堆栈溢出异常,类似于此 Oracle 支持文档中描述的异常:“当 IN 子句很长时,选择失败并出现 ORA-3113 (文档 ID 1351212.1)”。解决方案是更改 oracle.exe 的线程堆栈大小(这可能很危险),或者重写代码以避免过多的串联。

我能够在 Windows 10 Pro 上的 Oracle EE 19.3 上重现您的问题。虽然我只收到 PL/SQL 代码的错误,而不是 SQL 语句的错误。解决此问题很困难,因为 ORA-3113 错误确实会在警报日志中创建条目,也不会创建带有 ORA-600 错误的跟踪文件。我能找到的唯一记录是文件 orclcore.log 中的一个小条目。(我不确定文件名的“orcl”部分是基于 SID,还是始终是字面上的“orcl”。orclcore.log 文件包含以下行,这使我在 My Oracle Support 上找到了文档:“Excp.代码: 0xc00000fd Excp.类型:STACK_OVER标志:0x00000000”。

第一个建议的解决方案是更改 oracle.exe 线程的堆栈大小。尽管我能找到的有关此方法的消息来源警告说,它可能会导致其他问题,并且您需要记住在修补后重新应用更改。该修复将允许更多串联,但不一定允许无限量的串联。以下是在 Windows 上对我有用的(修改后的)说明:

  • 使用 NEXT 检查默认堆栈大小的当前值:orastack %ORACLE_HOME%\bin\oracle.exe
  • 停止数据库(在 Windows 上,使用类似“OracleServiceSID”的名称停止服务)
  • 使用下一个命令将默认堆栈大小修改为更高的值:orastack %ORACLE_HOME%\bin\oracle.exe <new_value>
  • 启动数据库(在 Windows 上,启动服务)
  • 测试

但我同意 Paul W 的观点,即您最好通过更改代码来避免这种问题,而不是尝试以官方方式修复。


如果您确实需要保留串联代码,但您也不能修改每个 oracle.exe 文件(例如,如果您使用的是 Amazon RDS 或 Oracle 自治数据库等托管数据库),则可能还有另一种解决方案。

我敢打赌,中断的代码是 PL/SQL 解析器的一部分。堆栈溢出在递归函数中最为常见,我敢打赌 Oracle 使用递归下降解析器,它为每个关键字调用一个函数。在 Oracle 代码的某个地方,我敢打赌,每次找到令牌时都会调用一个名为 C 函数的 C,并且每当同一语句中有另一个函数时,该函数都会调用自身。CONCAT_OPERATOR||||

由于该错误在解析器中,因此您只需要避免一次。在您可以完全控制的位置创建一个 Oracle 数据库。使用 orastack.exe 仅为该实例增加堆栈空间。将中断代码放入单个过程中,对其进行编译,然后[包装它]。包装源代码是为了混淆源代码,使其他人无法读取它。但这种混淆并不是什么超级安全的加密——它只是存储过程的字节码。

该字节代码不会再次被分析,因此在安装或运行新的包装过程时,不应发生分析器错误。

评论

1赞 ZeRoOo 11/8/2023
高超!谢谢你的帮助。我能够按照说明解决问题。我也尝试使用包装过程,但它没有用。编译时遇到同样的错误。