Is it possible to use interfaces with objects in plsql?
For example say I have a bunch of objects and want to sort them by date with a generic function. Could I have something like the following?
create or replace interface DateInterface
(
member function get_date return date
)
/
create or replace type TypeA implements DateInterface
(
my_date date,
member function get_date return date
)
/
create or replace type body TypeA is
member function get_date return date is
begin
return my_date;
end;
end;
/
create or replace type dateTable as table of DateInterface;
/
function EarliestDate (dates dateTable) returns date is
l_earliestDate date;
begin
l_earliestDate := dates(1);
for i in dates.first .. dates.last
loop
if l_earliestDate.get_date > dates(i).get_date then
l_earliestDate := dates(i);
end if;
end loop;
return l_earliestDate;
end;
I know I could have them inherit a class, but is there anything for doing this with an interface which would be more flexible?
Oracle supports interfacing external sources like Java and C languages within its procedures and functions using the LANGUAGE clause in them. Implement the date sorting logic of the objects in a procedure or a function and interface with other programming languages.
For reference, see Oracle's documentation
I'm late to the party but Oracle has sort of OO support now in PLSQL:
https://docs.oracle.com/en/database/oracle/oracle-database/18/adobj/inheritance-in-sql-object-types.html#GUID-A5FE7A3B-7C1C-430A-8095-76AE955119C9
You have inheritance etc. with that. So you could do sort of an interface for PLSQL Object Type Objects
But it comes with problems. Eg. storing in tables.
See https://oracle-base.com/articles/8i/object-types
Related
Is there any replacement for updateOnDuplicate option in bulckCreate method in sequelize ORM with NodeJs for oracle database . OtherWise Suggest a oracle procedure which accepts Array of Objects as Input Parameter With NodeJS , Sequelize ,Oracle .
**If I use below **
RoleModel.bulkCreate(data, {
updateOnDuplicate: ["role", "roleId", "ROLECODE"]
});
**Getting below Error
**
Error: oracle does not support the updateOnDuplicate option.
**For Oracle Procedure I Followed below
**
create or replace TYPE ROLE_OBJECT as object(
"ROLE" VARCHAR2(255 CHAR),
"ROLECODE" VARCHAR2 (255 CHAR),
"ROLEID" NUMBER(1)
);
create or replace TYPE ROLE_ARRAY is table of ROLE_OBJECT;
create or replace PROCEDURE ROLESUPDATING(useResultIn IN VARCHAR2)
IS
BEGIN
FOR i IN 1 .. useResultIn.count
LOOP
dbms_output.put_line(useResultIn.count);
END LOOP;
END;
BEGIN
ROLESUPDATING([{'role':'admin','rolecode':'adm','roleid':'1'}]);
END;
/*
It is not giving the length of the array
It is giving below Error
ORA-06531: Reference to uninitialized collection
ORA-06512: at "Collect.ROLESUPDATING", line 4
ORA-06512: at line 7
*/
[{prop:value}] are all javascript syntax, none of which makes any sense in PL/SQL. Instead, you can do this:
create or replace PROCEDURE ROLESUPDATING(useResultIn IN ROLE_ARRAY)
IS
BEGIN
FOR i IN 1 .. useResultIn.count
LOOP
dbms_output.put_line(useResultIn.count);
END LOOP;
END;
BEGIN
ROLESUPDATING(ROLE_ARRAY(ROLE_OBJECT('admin','adm',1)));
END;
Note that in addition to restructuring your actual call to ROLESUPDATING, it was also necessary to charge your parameter type from varchar2 to role_array (the collection type). In PL/SQL, you instantiate an object or collection by calling the type name like a function and passing in the property values like arguments. You can also nest the call to an object type and a collection type to create the collection in one statement, as in my answer.
I need get property name as string from object class. It is possible in Delphi?
I need property transfer as argument of method and get property name as string. I don't want use property name as argument because the compiler does not catch error when name of property is changed in class.
type
TMyClass = class
private
fField: some_type;
public
property Field:some_type read fField;
end;
function GetPropertyName(arg: ??):string
begin
Result := arg.PropertyName; // here I need get property name form transfer type
end;
var
obj: TMyClass;
name: string;
begin
name := GetPropertyName(obj.Field);
end;
To clarify, as discussed in comments, I'm looking for a direct equivalent to the C# nameof function.
From the comments you make it clear that you are looking for a Delphi equivalent to the C# nameof function.
No such equivalent exists in Delphi, and the language does not have facilities for you to create it yourself. Instead you will need to name the method as a string literal in code.
Recently at my company we tried to use DUnitX with all it's blessings to test classes we wrote. Since those classes reflect entities in database all fields have to accept null values as well as specific type (e. g. Integer or string).
Since spring4d already have those we tried to use them:
INaleznosc = interface
['{D5D6C901-3DB9-4EC2-8070-EB0BEDBC7B06}']
function DajPodstawaVAT(): TNullableCurrency;
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
TNaleznosc = class(TInterfacedObject, INaleznosc)
strict private
FId: TNullableInt64;
FPodstawaVAT: Currency;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
published
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
INaleznoscFunkcje = interface
['{509288AB-110A-4A52-BE93-3723E5725F4B}']
function DajPodstawaVAT(pID: TNullableInt64): TNullableCurrency;
end;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
begin
FPodstawaVAT := FFunkcje.DajPodstawaVAT(FId);
end;
procedure TTestNaleznosc.PodstawaVATGetterNieWywolujefunkcji();
var
funkcjeNaleznosc: TMock<INaleznoscFunkcje>;
klasa: INaleznosc;
id: TNullableInteger;
begin
//initialize tested elements
funkcjeNaleznosc := TMock<INaleznoscFunkcje>.Create();
id := 15;
klasa := TNaleznosc.Create(funkcjeNaleznosc, id, zmienne);
//setup expected behaviour from mock
funkcjeNaleznosc.Setup.WillReturn(2).When.DajPodstawaVAT(id);
funkcjeNaleznosc.Setup.Expect.Once.When.DajPodstawaVAT(id);
//this triggers getter
klasa.PodstawaVAT;
end;
When this code is executed we get AV exception First chance exception at $00000000. Exception class $C0000005 with message 'access violation at 0x00000000: access of address 0x00000000'. Process Tests.exe (6556).
Eventually we narrowed this issue down to Move procedure in System.Rtti unit, TValueDataImpl.ExtractRawDataNoCopy function:
when Length(FData) is less or equal to 8 it works fine
when Length(FData) is between 9 and 32 at line 5905 of System unit (FISTP QWORD PTR [EDX+8] {Save Second 8}) whole call stack disappears beside two lines (we are not sure whether it's relevant or not, but it doesn't look like good sign) and after getting to topmost function (according to call stack) we get error.
Call stack before "saving second 8"
Call stack after "saving second 8"
Is it our fault or is it some issue with system/spring/dunitx units? How can we use nullable types and tests at the same time?
I am not sure if Delphi Mocks has a generic type parameter on its WillReturn method but if so then pass TNullableCurrency there - otherwise the compiler will infer the type from the parameter 2 you are passing and obviously internally it fails to put that into the TNullableCurrency it should return.
If it does not and only allows TValue then you need to pass one that contains a TNullableCurrency and not 2 which it would by using its implicit operator like so: TValue.From<TNullableCurrency>(2)
Furthermore I am not sure if they did fix the code in the SameValue routine in Delphi Mocks when the value to be compared is a record (as TNullableCurrency is)
Edit: no, they did not - see https://github.com/VSoftTechnologies/Delphi-Mocks/issues/39
You might want to consider giving Spring4D mocks a try which should be able to handle nullables.
Here is what I am working with:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept in VARCHAR2 DEFAULT
'Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT',
'Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting',
Mgr in VARCHAR2 DEFAULT NULL) AS
vManager Varchar2(30) := Mgr;
vDepartment Varchar2(30) := Dept;
I want the default values to be that entire list of strings. Later in my code, I want the option to be able to specify a few managers, or a few departments, or not input anything, so that the entire list is returned:
WHERE m.first_name ||' ' || m.last_name IN (vManager) -- NULL if I don't input anything
OR d.department_name IN (vDepartment) -- All departments if I don't input anything
When I call the function, and don't input anything, I'm expecting to return all possible rows. That is the desired effect.
In order to be able to pass a list of strings as a parameter you should change the parameter to a list.
CREATE OR REPLACE TYPE t_departments IS TABLE OF VARCHAR2(30);
And then create your procedure based on that new type:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN t_departments DEFAULT t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting'), Mgr IN VARCHAR2 DEFAULT NULL) AS
Later you could use MEMBER OF instead of IN to check if a certain value is in the table:
...
WHERE d.department_name MEMBER OF Dept
...
Alternatively, if you can't change the type of the procedure parameters, you could just use variables:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN VARCHAR2 DEFAULT NULL, Mgr IN VARCHAR2 DEFAULT NULL) AS
TYPE t_departments IS TABLE OF VARCHAR2(30);
v_all_departments t_departments := t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting');
vManager VARCHAR2(30);
vDepartment t_departments;
BEGIN
IF Dept IS NULL THEN
vDepartment := v_all_departments;
ELSE
vDepartment := t_departments(Dept);
END IF;
...
WHERE d.department_name MEMBER OF vDepartment
...
END;
I think something is unclear here...
First, this is a procedure - not a function, therefor it will not return a thing.
A varchar is not an array.
When you are getting or returning a varchar2 - there is only one possible string to pass back and forth... If you want to use a list - you should check the array types PL/SQL offers.
http://docs.oracle.com/cd/A97630_01/appdev.920/a96624/05_colls.htm
Third - if you have constant values, in a database the place for it is within a table, not as constants in a procedure.
I would recommend you to check out Temporary tables - they're a great data facilitator for "within session" communication for a list that you would like to return. http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:5824706700346810645
These would be a great "in session"/"in connection" joins, like the one you described above (select ... where last_name in (select manager from temptable) for example).
Another thing - using the last name for identifying records in the DB is not the way you wanna go. You can use unique identifiers for that, and foreign keys. (if you are unfamiliar with the terms - What are database constraints? .
If I did not answer your question (or pointed you out to the answer) - please clarify your question, since I am not sure (probably as many others) what exactly you want to do (logically).
It's a quite unclear what you're asking but here is a minimal example how you can pass a list of values that can be used as a part of a SQL query to a PL/SQL subprogram:
-- SQL type so that can be used in SQL context (the select-statement)
create or replace type strlist_t is table of varchar2(4000);
/
create or replace procedure foo(p_owners in strlist_t default strlist_t('JANI')) is
begin
for i in (select * from all_tables
where owner in (select * from table(p_owners)))
loop
dbms_output.put_line(i.owner || '.' || i.table_name);
end loop;
end;
/
show errors
begin
-- define your own criteria
foo(strlist_t('SYS', 'JANI'));
-- use the default criteria
foo;
end;
/
Hope this helps !
I have the following situation.
We develop in DelphiXE.
We are putting the majority of our functions in a DATAMODULE.
function1 (database, transaction, paramInteger) : float
for example
function1 take parameters database (TIBDATABASE), the transaction TIBTRANSACTIOn and aditiona parameter integer. and return a float
function GetLastPretAch(DIBase : TIBDatabase; Tran : TIBTransaction; const aID : Integer) : Double;
var workQuery : TIBQuery;
begin
try
workQuery := TIBQuery.Create(Application);
try
workQuery.Close;
workQuery.Database := DIBase;
workQuery.Transaction := Tran;
workQuery.SQL.Clear;
workQuery.SQL.Add('SELECT * FROM GETLASTPRETACH(-1, :AARTNR)');
workQuery.ParamByName('AARTNR').AsInteger := aID;
workQuery.Open;
Result := workQuery.FieldByName('LASTPRET').AsFloat;
except
on e : Exception do begin
raise EMagisterException.Create(TranslateIbError(e));
end;
end;
finally
FreeAndNil(workQuery);
end;
end;
Now I want to use this functions from a thread. is this thread safe?
inside execute procedure like
ID := GetLastPretAch(database, transaction, 1);
is or not thread safe?
The answer to your question is Yes, you can use that function from inside a worker thread's execute procedure. You might want to consider refining your SQL to only SELECT the field LASTPRET instead of SELECT *.
For an extended discussion on what "thread safe" means refer to this SO question
What does threadsafe mean?
Looks like you're using IBX Components which, the last time I looked were most definitely NOT thread-safe. If you switched to a data access layer that was thread-safe, you should be fine with that code. FYI UIB (Unified Interbase components) are thread-safe.