【问题标题】:Buffer Overflow error on custom STRAGG function in OracleOracle 中自定义 STRAGG 函数的缓冲区溢出错误
【发布时间】:2016-11-08 20:40:02
【问题描述】:

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

从 TBL_CHANGE_SUMMARY 中选择 STRAGG('000 - ' || 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

【问题讨论】:

  • 那么,错误在哪一行?您必须在 prod 中有不同的数据,不是吗?
  • 您使用的是什么字符集?您能否运行此查询:select * from nls_database_parameters where parameter like '%SEMA%' or parameter like '%SET'; 并将其结果附加到问题中?
  • 错误行不会出现在 STRING_AGG_TYPE 中,但会出现在实际的 sql 中,这与我的第一个示例“select STRAGG('000 - ' || CHANGE_TEXT) from...”相同。是的,它是生产中的不同数据,但我不认为它不是数据,因为我探索了许多数据大小的场景,它总是在开发环境中工作。
  • 谢谢,已添加字符集信息。

标签: oracle overflow aggregation truncate


【解决方案1】:

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

【讨论】:

  • 感谢您的详细解释!一个简单的问题,所以底部的成功示例是使用 AL16UTF16?如果是这样,NLS_CHARACTERSET 是否应该与 NLS_NCHAR_CHRACTERSET 匹配?
  • 简而言之:VARCHAR2 使用 NLS_CHARACTERSENVARCHAR2 使用 NLS_NCHAR_CHARACTERSET。这是一个很大的话题,详细解释请参考文档:docs.oracle.com/cd/E11882_01/server.112/e10729/…
猜你喜欢
  • 2017-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-20
  • 1970-01-01
  • 2023-01-25
  • 1970-01-01
  • 2020-08-20
相关资源
最近更新 更多