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

时间:2016-07-06 17:23:47

标签: oracle overflow aggregation truncate

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

  

从TBL_CHANGE_SUMMARY选择STRAGG('' || CHANGE_TEXT)

这在开发oracle服务器上运行良好,但在生产环境中会出现以下错误。

  

ORA-06502:PL / SQL:数字或值错误:字符串缓冲区太小

以下是已修改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

1 个答案:

答案 0 :(得分:0)

您正在使用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.