Oracle 中自定义 STRAGG 函数的缓冲区溢出错误

Buffer Overflow error on custom STRAGG function in Oracle

我正在使用自定义 STRAGG,它在 4000 处截断字符串,如下所示,

select STRAGG('000 - ' || CHANGE_TEXT) from TBL_CHANGE_SUMMARY

这在开发 oracle 服务器中工作正常,但在生产环境中出现以下错误。

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

以下是修改了STRAGG的自定义代码,

CREATE OR REPLACE TYPE "STRING_AGG_TYPE" as object
(
  total varchar2(4000),
  static function ODCIAggregateInitialize(sctx IN OUT string_agg_type) return number,
  member function ODCIAggregateIterate(self  IN OUT string_agg_type,
                                       value IN varchar2) return number,
  member function ODCIAggregateTerminate(self        IN string_agg_type,
                                         returnValue OUT varchar2,
                                         flags       IN number) return number,
  member function ODCIAggregateMerge(self IN OUT string_agg_type,
                                     ctx2 IN string_agg_type) return number
);

CREATE OR REPLACE TYPE BODY "STRING_AGG_TYPE" is

  static function ODCIAggregateInitialize(sctx IN OUT string_agg_type ) return number is
  begin
    sctx := string_agg_type(null);

    return ODCIConst.Success;
  end;

  member function ODCIAggregateIterate(self  IN OUT string_agg_type,
                                       value IN varchar2) return number is
   BEGIN
      --prevent buffer overflow for more than 4,000 characters
     IF NVL(LENGTH(self.total), 0) + NVL(LENGTH(VALUE), 0) < 3930 THEN
        IF (self.total IS NULL) THEN
           self.total := VALUE;
        ELSIF INSTR ('~#~' || self.total || '~#~', '~#~' || VALUE || '~#~',1,1) = 0 AND INSTR ( self.total, '[TRUNCATED]',1,1) = 0 THEN
                self.total := self.total || '~#~' || VALUE;
        END IF;
     ELSE
        IF INSTR ( self.total, '[TRUNCATED]',1,1) = 0 THEN
            self.total := self.total || '[TRUNCATED]';
        END IF;
     END IF;

      RETURN ODCIConst.Success;
   END;


   MEMBER FUNCTION ODCIAggregateTerminate (self          IN     string_agg_type,
                                           returnValue      OUT VARCHAR2,
                                           flags         IN     NUMBER)
      RETURN NUMBER
   IS
   BEGIN
      IF INSTR ( self.total, '[TRUNCATED]',1,1) = 0 THEN
         returnValue := LTRIM (self.total, '~#~');
      ELSE
         IF (self.total IS NOT NULL) THEN

            returnValue := SUBSTR(self.total, 1, INSTR ( self.total, '[TRUNCATED]',1,1) - 1) || '..... Truncated because of system limitation';
         END IF;
      END IF;

      RETURN ODCIConst.Success;
   END;


  member function ODCIAggregateMerge(self IN OUT string_agg_type,
                                     ctx2 IN string_agg_type) return number is
  begin
    self.total := self.total || ctx2.total;
    return ODCIConst.Success;
  end;


end;

为什么相同的代码会有不同的表现?我需要在 Oracle 环境中更改或设置什么吗?

以下是其在各个环境中使用字符的区别,

Character set in Production,
NLS_CHARACTERSET          AL32UTF8
NLS_NCHAR_CHARACTERSET    UTF8
NLS_LENGTH_SEMANTICS      BYTE

Character set in Development,
NLS_CHARACTERSET        AL32UTF8
NLS_NCHAR_CHARACTERSET  AL16UTF16
NLS_LENGTH_SEMANTICS    BYTE

您使用的是 AL32UTF8 - 在 UTF-8 中,一些字符使用 1 个字节编码,而其他字符占用 2 个字节,有时占用 4 个字节。

您还使用 NLS_LENGTH_SEMANTICS = BYTE(这是默认设置)。
这意味着像 VARCHAR2(5) 这样的声明将字符串的长度
限制为最多 5 个字节 不5个字符.

使用VARCHAR2( 4000 char ),不简单VARCHAR2( 4000 )

请查看其工作原理示例:

select * 
from nls_database_parameters
where parameter like '%SEMA%'
  or parameter like '%SET';
PARAMETER                 VALUE              
------------------------- --------------------
NLS_LENGTH_SEMANTICS      BYTE                
NLS_NCHAR_CHARACTERSET    UTF8                
NLS_CHARACTERSET          AL32UTF8 

现在:

declare
   x varchar2(10);
begin
   x := 'ąśćz123456';
   dbms_output.put_line( x );
end;
/
ORA-06502: PL/SQL: błąd liczby lub wartości: character string buffer too small
ORA-06512: przy linia 4
06502. 00000 -  "PL/SQL: numeric or value error%s"
*Cause:    An arithmetic, numeric, string, conversion, or constraint error
           occurred. For example, this error occurs if an attempt is made to
           assign the value NULL to a variable declared NOT NULL, or if an
           attempt is made to assign an integer larger than 99 to a variable
           declared NUMBER(2).
*Action:   Change the data, how it is manipulated, or how it is declared so
           that values do not violate constraints.

但是:

declare
   x varchar2(10 char);
begin
   x := 'ąśćz123456';
   dbms_output.put_line( x );
end;
/

ąśćz123456

PL/SQL procedure successfully completed.