How to bind calls in an object of type pl / sql returning SELF

I want the oracle object to return itself and be able to bind these calls. How to do it?

I tried to return the same type, but it does not work, I also tried to add a procedure that is called by the funcition function, but it does not work either. Always complains about changing the value of the width element. It seems that the functions do not allow side effects ?, are they modeled after the more mathematical principle of the function? Is this achievable ?. I think I could write a function so that it creates a new rectangle with SELF, but this is so much work.

My goal is to be able to bind calls like jQuery or some java classes (singleton?). Sort of:

r := r.setWidth(0).setWidth(1).setWidth(2);

Of course, he will have more methods, and it will not be a rectangle. This is mistake:

 Error: PLS-00363: expression 'SELF' cannot be used as an assignment target Line: 18 Text: stWidth(w); 

-

 CREATE OR REPLACE TYPE rectangle AS OBJECT ( -- The type has 3 attributes. length NUMBER, width NUMBER, area NUMBER, -- Define a constructor that has only 2 parameters. CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT, MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle, MEMBER PROCEDURE stWidth(w NUMBER) ) 

-

 CREATE OR REPLACE TYPE BODY rectangle AS CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.length := length; SELF.width := width; -- We compute the area rather than accepting it as a parameter. SELF.area := length * width; RETURN; END; MEMBER PROCEDURE stWidth(w NUMBER) IS BEGIN self.width := w; END; MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS BEGIN stWidth(w); RETURN SELF; END; END; 

Thanks in advance.

+4
source share
4 answers

You cannot change an object and assign it to it at the same time. You already know the solution, "create a new rectangle with SELF." But that will not be a big job.

Replace this:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS BEGIN stWidth(w); RETURN SELF; END; 

with this:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS v_rectangle rectangle := self; BEGIN v_rectangle.width := w; RETURN v_rectangle; END; 

You actually got a compilation error. By default, SELF is an IN parameter. The call to stWidth failed because it changed the IN parameter with self.width := w; .

See: http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjbas.htm#CHDCFEEE

SELF is always the first parameter passed to the method.

  • In member functions, if SELF is not declared, its default parameter mode is IN.

  • In member procedures, if SELF is not declared, its default parameter mode is IN OUT. The default behavior does not include the NOCOPY compiler hint.

+6
source

Sorry, I'm a little late, but the other answers are incorrect, at least using Oracle 11gR2, and what you are trying to achieve is really possible.

I just stumbled upon a problem recently, and I see that can finally return the SELF link just like you tried , without compromising or applying workarounds.

The only thing needed is to override the method (explicitly) by setting the (implicit) SELF parameter as SELF IN OUT rectangle .
SELF is the leading parameter, which is silently passed to each object, and for functions it is defined as IN (immutable, this is what seems to make the compiler complain). This is similar to compilation time, but the good part is that when you call a method, you can omit it.
In the example at the end of the message (slightly rewritten from yours), we define

 MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle 

and we execute it without a SELF link:

 declare r rectangle := rectangle(1,2); begin dbms_output.put_line('width is: ' || r.width); dbms_output.put_line('new width is: ' || r.incrementWidth(3).width); end; / 

Please note that there are two warnings to be aware of.

warning 1

Each method call temporarily creates a new copy of the object.
But only temporarily, the new instance is short-lived, only between the beginning and the end of the method. This is due to the use of IN OUT parameters for all functions or procedures and does not apply to object types. If you want to prevent this behavior, you can override the signature of your function using the NOCOPY hint:

 MEMBER FUNCTION incrementWidth(SELF IN OUT NOCOPY rectangle, w NUMBER) RETURN rectangle 

See ORACLE-BASE - NOCOPY for more details . Please note that this is a hint, but this does not guarantee that you are finally using the same object reference and not just the newly created object, so use it with caution.

warning 2

Given that you raised this question, it is likely that you have an OOP background, and you might get a surprise when trying to call a method without using a return link, such as

 r.incrementWidth(10); 

The compiler will return an error:

 PLS-00221: 'INCREMENTWIDTH' is not a procedure or is undefined 

So what is going on here? Well, the so-called "static polymorphism" (i.e. choosing the method overload at compile time) in pl / sql is slightly different from other OOP languages, since it even takes into account the use of the RETURNed type. To solve this problem, add a companion procedure with a signature whose only difference is the absence of a return type:

 MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle, MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) 

It is reasonable if you do not want to duplicate the same code in the function and procedure, the procedure will internally delegate the function; and depending on the version of Oracle you are using, you can play with code embedding (see OCP: new PL / SQL functions ) to achieve the same speed as a copy implementation (you hardly notice the real difference). Explicit "inserts" of points in a method by name, however, it also works in this case when the method name is overloaded.

In the example below, you will see that either the function or the procedure is alternatively called depending on the use of the returned / not _returned parameter.

so finally ...

The code you want to write is as follows: I did not use NOCOPY , so as not to contaminate the relevant materials, but it is easy to do)

 CREATE OR REPLACE TYPE rectangle AS OBJECT ( length NUMBER, width NUMBER, CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT, MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle, MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) ); / CREATE OR REPLACE TYPE BODY rectangle AS CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.length := length; SELF.width := width; RETURN; END; MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle IS BEGIN dbms_output.put_line('...invoking the function with input ' || w); width := width + w; RETURN SELF; END; MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) IS BEGIN PRAGMA INLINE (incrementWidth, 'YES'); dbms_output.put_line('...invoking the procedure with input ' || w || ', that in turn is...'); self := incrementWidth(w); END; END; / 

After doing ...

 set serveroutput on select * from v$version where rownum = 1; declare r rectangle := rectangle(1,2); begin dbms_output.put_line('width is: ' || r.width); --this is invoking the "function" version, because we are making use of --the returned rectangle object dbms_output.put_line('new width is: ' || r.incrementWidth(3).width); --the original reference has been updated even without using the NO COPY hint dbms_output.put_line('original object has width updated: ' || r.width); --this is invoking the "procedure" version, because we are not using the returned object r.incrementWidth(3); --of course this has finally worked as well dbms_output.put_line('again what is the new width like now?: ' || r.width); end; / 

You are getting

 BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production width is: 2 ...invoking the function with input 3 new width is: 5 original object has width updated: 5 ...invoking the procedure with input 3, that in turn is... ...invoking the function with input 3 again what is the new width like now?: 8 
+1
source

You cannot return SELF from a member function. You can create a copy, but I'm not sure why the setwidth function will return a rectangle object. I know that this basic example probably came from some old Oracle docs, but I would not put the computed field (scope) as an attribute. If computed, it must be a member function (or procedure). The reason is that if you do not want to always remember about updating the area in the functions that affect the area, you will shoot in the foot. Your example is broken (except for the selfWidth returned by selfWidth), since only the constructor calculates the area.

 set serveroutput on declare r rectangle := rectangle(3,2); begin dbms_output.put_line('Area is: ' || r.area); -- change the width r.stWidth(4); dbms_output.put_line('Area is: ' || r.area); end; 

Output:

 Area is: 6 Area is: 6 

This is obviously not true. I would do something like:

 CREATE OR REPLACE TYPE rectangle AS OBJECT ( -- The type has 2 attributes. len NUMBER, width NUMBER, -- Define a constructor that has only 2 parameters. CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER) RETURN SELF AS RESULT, MEMBER PROCEDURE setLength(l NUMBER), MEMBER PROCEDURE setWidth(w NUMBER), MEMBER FUNCTION getArea return NUMBER, MEMBER PROCEDURE showArea ); CREATE OR REPLACE TYPE BODY rectangle AS CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER) RETURN SELF AS RESULT AS BEGIN SELF.len := len; SELF.width := width; RETURN; END; MEMBER PROCEDURE setLength(l NUMBER) IS BEGIN self.len := l; END; MEMBER PROCEDURE setWidth(w NUMBER) IS BEGIN self.width := w; END; MEMBER FUNCTION getArea return NUMBER IS BEGIN return self.len * self.width; END; MEMBER PROCEDURE showArea IS BEGIN -- Just shows how we calculated area and spits to console dbms_output.put_line('Area is: ' || self.getArea || ' (' || self.len || ' * ' || self.width || ')'); END; END; 

And so you will have:

 set serveroutput on declare r rectangle := rectangle(3,2); begin dbms_output.put_line('Area is: ' || r.getArea); -- change the width r.setWidth(4); dbms_output.put_line('Area is: ' || r.getArea); end; 

Output:

 Area is: 6 Area is: 12 
0
source

I ran into the same problem, and since the top answers disagree, I had to experiment.

I have the following code that will not work even though Antonio's answer says:

 create or replace type tst as object ( inner_value varchar2(100), constructor function tst return self as result, member function update_value( self in out nocopy tst, p_value varchar2) return tst, member procedure print_value(self in out nocopy tst) ) final; / create or replace type body tst as constructor function tst return self as result is begin self.inner_value := 'DEFAULT'; return; end; member function update_value( self in out nocopy tst, p_value varchar2) return tst is begin self.inner_value := p_value; return self; end; member procedure print_value(self in out nocopy tst) is begin dbms_output.put_line(self.inner_value); end; end; / 

The following code will not work whether the self parameter is specified using nocopy or not.

 set serveroutput on; begin tst().update_value('TEST').print_value; end; / 

If print_value redefined as a member procedure print_value(self in tst) , then tst().print_value; becomes valid.

The scannot constructor must be encoded if the self in out or in out nocopy parameter, whether you return the copy or self , you always get PLS-00363 . The only way to start nested calls from the constructor is to use self in parameters everywhere and make a copy, which is disappointing:

 create or replace type tst as object ( inner_value varchar2(100), constructor function tst return self as result, member function update_value( self in tst, p_value varchar2) return tst, member procedure print_value(self in tst) ) final; / create or replace type body tst as constructor function tst return self as result is begin self.inner_value := 'DEFAULT'; return; end; member function update_value( self in tst, p_value varchar2) return tst is a_copy tst; begin a_copy := tst; a_copy.inner_value := p_value; return a_copy; end; member procedure print_value(self in tst) is begin dbms_output.put_line(self.inner_value); end; end; / 

Then it prints a TEST as expected:

 set serveroutput on; begin tst().update_value('TEST').print_value; end; / 

If you can divide the operator by t := tst(); t.update_value('TEST').print_value; t := tst(); t.update_value('TEST').print_value; , then it works even with in out nocopy self parameters.

0
source

Source: https://habr.com/ru/post/1495579/


All Articles