Question

i wrote a simple aggregate function that should work on columns with csv text in them by aggregating distinct values in a resulting csv string. the functions seems to work all the way to the end when it craps out with the ORA-06502 error right when it should be returning the result.

here is the code:

type def:

create or replace type array_union_typ as object (

union_agg nvarchar2(1000),

static function ODCIAggregateInitialize(sctx  in out array_union_typ)
                return number,

member function ODCIAggregateIterate   (self  in out array_union_typ,
                                        value in nvarchar2)
                return number,

member function ODCIAggregateTerminate (self         in array_union_typ,
                                        return_value out nvarchar2,
                                        flags        in number)
                return number,

member function ODCIAggregateMerge(self in out array_union_typ,
                                   ctx2 in array_union_typ)
                return number,

static function agg_union(arg1 in nvarchar2,
                          arg2 in nvarchar2)
                return nvarchar2
);

the body:

create or replace type body array_union_typ  is

static function ODCIAggregateInitialize(sctx in out array_union_typ
                                     ) return number is
begin
    sctx := array_union_typ(null);
    return ODCIConst.Success;
end;

member function ODCIAggregateIterate(self in out array_union_typ,
                                     value in nvarchar2
                                     ) return number is
begin
    union_agg := array_union_typ.agg_union(union_agg, value);
    return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(self in array_union_typ,
                                       return_value out nvarchar2,
                                       flags in number
                                       ) return number is
begin
    dbms_output.put_line('result: '''|| union_agg || ''', length:' || length(union_agg)); --this still prints
    return_value := self.union_agg; -- <-- this is where the error is indicated
    --return_value := 'x'; -- returning this still gives the error.
    return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self in out array_union_typ,
                                   ctx2 in array_union_typ
                                   ) return number is
begin
    union_agg := array_union_typ.agg_union(union_agg, ctx2.union_agg);
    return ODCIConst.Success;
end;

static function agg_union(arg1 in nvarchar2,
                          arg2 in nvarchar2)
                return nvarchar2 is
    result nvarchar2(1000);
    orig nvarchar2(1000);
begin
    dbms_output.enable;
    orig := replace(arg1||','||arg2, chr(0));

    FOR rec IN (SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
           FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a) LOOP
      IF result IS NOT NULL THEN
        result := result || ',' || rec.a;
      ELSE
        result := rec.a;
      END IF;
    END LOOP;
    --dbms_output.put_line('endwith: ''' || result || '''');
    RETURN substr(result,1,1000);
end;
end;

here is a test table and data:

SQL> desc uniontest
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 I                                                  NVARCHAR2(50)

SQL> select * from uniontest;

I
--------------------------------------------------
a
a
b,c
b,d,e

and finally, this is what happens if i try to use the aggregate function:

SQL> select array_union(i) from uniontest;
select array_union(i) from uniontest
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "M35456.ARRAY_UNION_TYP", line 25


result: 'a,b,c,d,e', length:9

if i simply pass a single character string like 'x' in the offending line, i still get the same error. only on a null result does it go away. i am stumped and out of ideas.

thanks for any help.

btw, if anyone has any idea why i get added \0 characters i my agg_union function parameters, i am dying to know about that, too.

Was it helpful?

Solution

The ODCIaggregate methods don't seem to be playing nicely with nvarchar2; you don't appear to be doing anything wrong since it works fine if those are all changed to varchar2.

If you are on 11gR2 and thus have the listagg function available - but currently aren't using that because you need to figure out the distinct values - you can combine it with your existing regexp_substr function, so you don't need your own type/function:

SELECT REPLACE(LISTAGG(a, ',') WITHIN GROUP (ORDER BY a), chr(0), '')
FROM (
    SELECT DISTINCT(regexp_substr(i, '[^,]+', 1, level)) AS a
    FROM uniontest CONNECT BY regexp_substr(i, '[^,]+', 1, level) IS NOT NULL
    ORDER BY a
);

... which with your data gives:

REPLACE(LISTAGG(A,',')WITHINGROUP(ORDERBYA),CHR(0),'')
------------------------------------------------------
a,b,c,d,e

In earlier versions you can use SELECT REPLACE(WM_CONCAT(a), chr(0), '') instead.

(The chr(0) here and the \0 in your question seem to be related to nvarchar2, but someone else will need to chip in on the details...)

OTHER TIPS

If I'm not mistaken this

 SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
       FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a) 

results in an infinite number of rows, since the CONNECT BY condition is not dependend on the row.

Since you loop over the result appending characters to a string sooner the string becomes to large.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top