I'm trying to learn how to create a user defined aggregate function. So far, I've been able to create one that compiles fine, but calling it gives an unexpected result. The function is a very simple test function that looks through a number of rows that are either set to 'Y' or 'N' and returns 'Y' if all are set to 'Y' and otherwise returns 'N'. I'm running it on a single row and getting back a blank varchar 2 instead.
I'm not sure what is the procedure to go through with debugging this. I've tried using DBMS_OUTPUT.PUT_LINE(), but I cannot see anything on the database output. The largest problem is that it is creating the function fine, and most of the code is in an object type. Thus, if I were to try to debug the select statement, it is calling code on the database that has already been compiled.
Below is the code for the function, but I don't want to know why this isn't working as much as I want to know how to debug so I can solve these issues myself, especially when more complex aggregate functions are involved.
CREATE OR REPLACE TYPE MYSCHEMA.ALL_TRUE_T AS OBJECT
(
TRUE_SO_FAR VARCHAR2(1),
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT ALL_TRUE_T) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(self IN ALL_TRUE_T, returnValue OUT VARCHAR2, flags IN NUMBER) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT ALL_TRUE_T, ctx2 IN ALL_TRUE_T) RETURN NUMBER
);
CREATE OR REPLACE TYPE BODY MYSCHEMA.ALL_TRUE_T IS
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT ALL_TRUE_T)
RETURN NUMBER IS
BEGIN
sctx := ALL_TRUE_T('Y');
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2)
RETURN NUMBER IS
BEGIN
IF value <> 'Y' OR self.TRUE_SO_FAR <> 'Y' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(self IN ALL_TRUE_T, returnValue OUT VARCHAR2, flags IN NUMBER)
RETURN NUMBER IS
BEGIN
returnValue := self.TRUE_SO_FAR;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT ALL_TRUE_T, ctx2 IN ALL_TRUE_T)
RETURN NUMBER IS
BEGIN
IF ctx2.TRUE_SO_FAR = 'N' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
END;
CREATE OR REPLACE PACKAGE MYSCHEMA.ALL_TRUE_PKG IS
FUNCTION ALL_TRUE (input VARCHAR2) RETURN VARCHAR2;
END;
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.ALL_TRUE_PKG IS
FUNCTION ALL_TRUE (input VARCHAR2) RETURN VARCHAR2
AGGREGATE USING ALL_TRUE_T;
END;
And here is how I call it. YN_TEST_TABLE currently has a single row with an 'N' in it.
SELECT
MYSCHEMA.ALL_TRUE_PKG.ALL_TRUE(YN)
FROM
MYSCHEMA.YN_TEST_TABLE
Finally, I'm not sure if this is relevant, but I'm using Toad 11.6.
Edit:
So I've tried inserting into a temp log table and that didn't work either.
I added the following
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2)
RETURN NUMBER IS
BEGIN
BEGIN
INSERT INTO MYSCHEMA.LAWTONFOGLES_TEMP_LOG
(
ID,
Message,
Time
)
VALUES
(
'all_true',
'test1',
systimestamp
);
END;
IF value <> 'Y' OR self.TRUE_SO_FAR <> 'Y' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
There was nothing in the temp log, but also no error message. It is as if none of the 4 aggregate function parts are even being called.
EDIT2:
So, to make things more interesting, this works when it is not in a package.
I did the following
CREATE OR REPLACE FUNCTION MYSCHEMA.LAWTONFOGLES_ALL_TRUE (input VARCHAR2) RETURN VARCHAR2
AGGREGATE USING ALL_TRUE_T;
and then ran this
SELECT
MYSCHEMA.LAWTONFOGLES_ALL_TRUE(YN)
FROM
MYSCHEMA.YN_TEST_TABLE
and got the results I expected. It seems that the code itself isn't a problem, but putting it in a package causes it to break. Thursday my Oracle DBA will be opening a ticket up with oracle, so I'll be sure to update with why does putting this in a package break it but leaving it as just a function doesn't when they get back with us. Until then I may just have to keep this outside of a package.
Also, I tried to add a put_line on it when it was working and still did not get an output. I think that the way user defined aggregate functions work prevent put_line from working.
CASTCOLLECTinstead of Oracle Data Cartridge, as I explain here.