2

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.

2
  • 1
    DBMS_OUTPUT.PUT_LINE should work, you may need to enable your client to view it. On SQL*Plus, this would be "set serveroutput on", on Toad there is probably some button to click to enable DBMS Output. But you're probably better off using CAST COLLECT instead of Oracle Data Cartridge, as I explain here. Commented Aug 13, 2013 at 18:20
  • See this SO Question for a similar discussion, and the followup Oracle forum post. This looks like an unsupported feature which will hopefully get resolved in a future release. Commented Aug 15, 2013 at 9:42

1 Answer 1

1

If you're using TOAD, be sure to turn on DBMS_OUTPUT recording before you run your proc so you can see your outputs. It should be on the bottom DBMS tab (if you have it open). Typically you'll see a red circle since it's defaulted as off. Click the circle so that it's green.

See this link as an example: http://geekbrigade.wordpress.com/2009/04/09/how-to-set-and-view-dbms-output-of-oralce-in-toad/

Sign up to request clarification or add additional context in comments.

8 Comments

Yeah, I already did that. Funny thing; this was the very first suggestion that my Oracle DBA gave when I contacted him. Currently going to try inserting into a temp log table.
Just to get the obvious out of the way - did you put an output as the first line? ... just to eliminate the possibility of the DBMS being off. Also, with Toad, I've found myself needing to turn it on and off sometimes. For whatever reason sometimes it doesn't kick in.
I added BEGIN DBMS_OUTPUT.PUT_LINE('BEGIN'); END; before the select and I do see the output from it, but not from any of the other outputs.
So your debugging is working then. There's just something that's stopping your proc prematurely. You may need to add more outputs to find out exactly where it's stopping, but, in general, you have a proper debug setup going.
I have an output set up at the very first line of my own code that will run with no results. Also did an insert into a temp table with no results.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.