Record type reflection / populating a record variable of an unknown type (PL/SQL) - oracle

I have a package with a record type and a variable of that type in it:
CREATE PACKAGE my_package AS
TYPE t_rec IS RECORD(
id NUMBER,
name VARCHAR2(100),
last_updated DATE
);
v_var t_rec;
END;
I want to create an universal procedure for populating any record variable, so it can be called like this:
BEGIN
populate_record('my_package.v_var', 'MY_TABLE', CHARTOROWID(:VAR1));
END;
The logic of that procedure may look like this:
PROCEDURE populate_record(
p_var_name IN VARCHAR2,
p_table_name IN VARCHAR2,
p_rowid IN ROWID
) IS
BEGIN
-- determining a type of a given record variable
-- determining names and data types of given record variable's columns
-- selecting a row from a given table by a given rowid
-- filling out a given record variable by mapping table columns
-- to record columns by their names (using dynamic PL/SQL)
END;
But I have no idea how to obtain information about a given record. Are there any data dictionary views or built-in functions for querying record type columns?
Thanks.
P.S.: 11.2
UPDATED
I found a way to obtain info about records using PL/Scope:
CREATE OR REPLACE VIEW user_record_types AS
SELECT
i.object_name package_name,
i.name type_name
FROM
user_identifiers i
WHERE
i.object_type = 'PACKAGE'
AND i.type = 'RECORD'
AND i.usage = 'DECLARATION'
/
CREATE OR REPLACE VIEW user_record_types_columns AS
SELECT
i1.object_name package_name,
i1.name type_name,
ROW_NUMBER() OVER (
PARTITION BY
i1.object_name,
i1.name
ORDER BY
i2.usage_id
) column_num,
i2.name column_name,
i3.name column_type,
i3.type column_type_class
FROM
user_identifiers i1,
user_identifiers i2,
user_identifiers i3
WHERE
i1.object_type = 'PACKAGE'
AND i1.type = 'RECORD'
AND i1.usage = 'DECLARATION'
AND i2.object_name = i1.object_name
AND i2.object_type = i1.object_type
AND i2.type = 'VARIABLE'
AND i2.usage = 'DECLARATION'
AND i2.usage_context_id = i1.usage_id
AND i3.object_name = i2.object_name
AND i3.object_type = i2.object_type
AND i3.usage = 'REFERENCE'
AND i3.usage_context_id = i2.usage_id
/
CREATE OR REPLACE VIEW user_record_variables AS
SELECT
i1.object_name package_name,
i1.name variable_name,
CASE i2.type
WHEN 'RECORD' THEN
i2.object_name
WHEN 'PACKAGE' THEN
i2.name
END type_package_name,
CASE i2.type
WHEN 'RECORD' THEN
i2.name
WHEN 'PACKAGE' THEN
i3.name
END type_name
FROM
user_identifiers i1,
user_identifiers i2,
user_identifiers i3
WHERE
i1.object_type = 'PACKAGE'
AND i1.type = 'VARIABLE'
AND i1.usage = 'DECLARATION'
AND i2.object_name = i1.object_name
AND i2.object_type = i1.object_type
AND i2.type IN ('RECORD', 'PACKAGE')
AND i2.usage = 'REFERENCE'
AND i2.usage_context_id = i1.usage_id
AND i3.object_name (+) = i2.object_name
AND i3.object_type (+) = i2.object_type
AND i3.type (+) = 'RECORD'
AND i3.usage (+) = 'REFERENCE'
AND i3.usage_context_id (+) = i2.usage_id
/
But there is a problem when I use %ROWTYPEs, because there is no info about "what of" that %ROWTYPE is. So I think that PL/Scope is not a complete solution...

You can try to use table user_identifiers or all_identifiers if you have the access, as this other SO post shows.
You may need to recompile your packages:
alter package my_types compile plscope_settings='IDENTIFIERS:ALL' reuse settings;
Unfortunately this is only available from 11gR1.

Related

How to modify column data-type while column is in used

I have problem while I use SELECT query. Unfortunettly, the ID is store as VARCHAR which is big mistake and right now I have problem in following function
FUNCTION GET_SUB_PROJECTS(p_currentUserId IN VARCHAR,p_projectId IN INT)
RETURN SYS_REFCURSOR IS
rc SYS_REFCURSOR;
-- getSubProject
BEGIN
OPEN rc FOR
SELECT
p.*,
a.number_ AS activityNumber,
a.description AS activityDescription
FROM
projects p
LEFT JOIN
project_users_schedule_dates pusd
ON
pusd.ProjectID = p.ProjectID AND pusd.UserID = p_currentUserId
LEFT JOIN
activities a
ON
a.id = p.activity
LEFT JOIN
responsible_persons rp
ON
rp.ProjectID = p.ProjectID AND rp.UserID = p_currentUserId
LEFT JOIN
users u
ON
u.UserID = p_currentUserId
WHERE
(u.User_roleID = 1 AND
p.CustomName LIKE CONCAT((SELECT CustomName FROM projects pr WHERE pr.ProjectID = p_projectId), '%') AND p.ProjectID <> p_projectId)
OR
((
(p.Responsible_person_id = p_currentUserId OR p.Delivery_contact = p_currentUserId OR rp.UserID = p_currentUserId OR (pusd.UserID = p_currentUserId AND SYSTIMESTAMP BETWEEN TO_DATE(pusd.StartDate,'YYYY-MM-DD') AND TO_DATE(pusd.EndDate,'YYYY-MM-DD') + INTERVAL '1' DAY AND
SYSTIMESTAMP BETWEEN TO_DATE(p.StartDate,'YYYY-MM-DD') AND TO_DATE(p.EndDate,'YYYY-MM-DD') + INTERVAL '1' DAY))
)
AND
p.CustomName LIKE CONCAT((SELECT CustomName FROM projects pr WHERE pr.ProjectID = p_projectId), '%') AND p.ProjectID <> p_projectId)
ORDER BY p.CustomName;
RETURN rc;
END GET_SUB_PROJECTS;
When I call function it needs to return data, but it doesn't. Somehow, here p.Responsible_person_id and p.Delivery_contact needs to be NUMBER but somehow it is VARCHAR
When I call function I use
SELECT PROJECT_PACKAGE.GET_SUB_PROJECTS('199',141) FROM DUAL
I found one solution to modify but It throw me error like
Error report -
ORA-01439: column to be modified must be empty to change datatype
01439. 00000 - "column to be modified must be empty to change datatype"
What to do in this situation ? What is the best method to solve this issue ?
You can change the data type of a column online using dbms_redefinition
create table t (
c1 varchar2(10)
primary key
);
create table t_new (
c1 number(10, 0)
primary key
);
insert into t values ( '1.0000' );
commit;
begin
dbms_redefinition.start_redef_table (
user, 't', 't_new',
col_mapping => 'to_number ( c1 ) c1',
options_flag => dbms_redefinition.cons_use_rowid
);
end;
/
exec dbms_redefinition.sync_interim_table ( user, 't', 't_new' );
exec dbms_redefinition.finish_redef_table ( user, 't', 't_new' );
desc t
Name Null? Type
C1 NOT NULL NUMBER(10)
select * from t;
C1
1
There are also options to copy constraints, triggers, indexes, etc. automatically in this process.

Retrieve argument details defined in private function/procedure [duplicate]

I want to obtain a list with all private procedures/functions from a package body.
For public object it is easy but I have no idea how to do that for private objects.
The nature of private functions is that they are private. There are no data dictionary views which expose them by default. USER_PROCEDURES and USER_ARGUMENTS only show information for public procedures (the ones defined in a package spec0.
However, we can get information about them using PL/SCOPE, but doing so requires a little bit of additional effort:
SQL> alter session set plscope_settings='IDENTIFIERS:ALL';
SQL> alter package your_package compile body;
Now you can find your private program units with this query:
select ui.type, ui.name, ui.usage_id
from user_identifiers ui
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DEFINITION'
and ui.type in ('PROCEDURE', 'FUNCTION')
minus
( select 'PROCEDURE', upr.procedure_name
from user_procedures upr
where upr.object_name = 'YOUR_PACKAGE'
union
select 'FUNCTION', uarg.object_name
from user_arguments uarg
where uarg.package_name = 'YOUR_PACKAGE'
and uarg.position = 0
);
To get the arguments of a private procedure plug the USAGE_ID from the previous query into this query:
select ui.name
, ui.type
, ui.usage_id
, ui2.type as param_datatype
from user_identifiers ui
left join user_identifiers ui2
on ui2.usage_context_id = ui.usage_id
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DECLARATION'
and ui.usage_context_id = :private_proc_usage_id
/
This needs to be a left join because user_identifiers has datatype entries for scalar datatypes (character, number, date, clob) but not complex datatypes (xmltype, user-defined types).
We can get lots of information about procedures from PL/SCOPE, even though it is not as easy as querying USER_PROCEDURES or USER_ARGUMENTS (in fact, it is surprisingly clunky). Find out more. Be aware that PL/SCOPE data is stored on the SYSAUX tablespace, so don't get into hot water with your DBA!

To extract private arguments details from procedure and function in Oracle [duplicate]

I want to obtain a list with all private procedures/functions from a package body.
For public object it is easy but I have no idea how to do that for private objects.
The nature of private functions is that they are private. There are no data dictionary views which expose them by default. USER_PROCEDURES and USER_ARGUMENTS only show information for public procedures (the ones defined in a package spec0.
However, we can get information about them using PL/SCOPE, but doing so requires a little bit of additional effort:
SQL> alter session set plscope_settings='IDENTIFIERS:ALL';
SQL> alter package your_package compile body;
Now you can find your private program units with this query:
select ui.type, ui.name, ui.usage_id
from user_identifiers ui
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DEFINITION'
and ui.type in ('PROCEDURE', 'FUNCTION')
minus
( select 'PROCEDURE', upr.procedure_name
from user_procedures upr
where upr.object_name = 'YOUR_PACKAGE'
union
select 'FUNCTION', uarg.object_name
from user_arguments uarg
where uarg.package_name = 'YOUR_PACKAGE'
and uarg.position = 0
);
To get the arguments of a private procedure plug the USAGE_ID from the previous query into this query:
select ui.name
, ui.type
, ui.usage_id
, ui2.type as param_datatype
from user_identifiers ui
left join user_identifiers ui2
on ui2.usage_context_id = ui.usage_id
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DECLARATION'
and ui.usage_context_id = :private_proc_usage_id
/
This needs to be a left join because user_identifiers has datatype entries for scalar datatypes (character, number, date, clob) but not complex datatypes (xmltype, user-defined types).
We can get lots of information about procedures from PL/SCOPE, even though it is not as easy as querying USER_PROCEDURES or USER_ARGUMENTS (in fact, it is surprisingly clunky). Find out more. Be aware that PL/SCOPE data is stored on the SYSAUX tablespace, so don't get into hot water with your DBA!

Retreive a list of private procedures/functions from a package body

I want to obtain a list with all private procedures/functions from a package body.
For public object it is easy but I have no idea how to do that for private objects.
The nature of private functions is that they are private. There are no data dictionary views which expose them by default. USER_PROCEDURES and USER_ARGUMENTS only show information for public procedures (the ones defined in a package spec0.
However, we can get information about them using PL/SCOPE, but doing so requires a little bit of additional effort:
SQL> alter session set plscope_settings='IDENTIFIERS:ALL';
SQL> alter package your_package compile body;
Now you can find your private program units with this query:
select ui.type, ui.name, ui.usage_id
from user_identifiers ui
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DEFINITION'
and ui.type in ('PROCEDURE', 'FUNCTION')
minus
( select 'PROCEDURE', upr.procedure_name
from user_procedures upr
where upr.object_name = 'YOUR_PACKAGE'
union
select 'FUNCTION', uarg.object_name
from user_arguments uarg
where uarg.package_name = 'YOUR_PACKAGE'
and uarg.position = 0
);
To get the arguments of a private procedure plug the USAGE_ID from the previous query into this query:
select ui.name
, ui.type
, ui.usage_id
, ui2.type as param_datatype
from user_identifiers ui
left join user_identifiers ui2
on ui2.usage_context_id = ui.usage_id
where ui.object_name = 'YOUR_PACKAGE'
and ui.usage = 'DECLARATION'
and ui.usage_context_id = :private_proc_usage_id
/
This needs to be a left join because user_identifiers has datatype entries for scalar datatypes (character, number, date, clob) but not complex datatypes (xmltype, user-defined types).
We can get lots of information about procedures from PL/SCOPE, even though it is not as easy as querying USER_PROCEDURES or USER_ARGUMENTS (in fact, it is surprisingly clunky). Find out more. Be aware that PL/SCOPE data is stored on the SYSAUX tablespace, so don't get into hot water with your DBA!

Oracle data modeler: How Create prodcedure and function

How create procedure e.g.:
Create PROCEDURE il_klub as (
select
sum(krk.kluby_id),
r.nazwa
FROM
rozgrywki_klubowe r,
kluby_roz_klub krk
WHERE
r.id = krk.rozgrywki_klubowe_id
GROUP by r.nazwa
)
and function e.g.:
DECLARE #mistrz TABLE (nazwa varchar, rozgrywki varchar)
INSER INTO #mistrz (nazwa, rozgrywki)
select
k.nazwa, r.nazwa
from
kluby k,
rozgrywki_klubowe r,
kluby_roz_klub krk,
historia_roz_klub hrk,
where
k.id = krk.kluby_id and k.id = hrk.kluby_id and r.id = krk.rozgrywki_klubowe_id
and r.id = hrk.rozgrywki_klubowe_id
and hrk.miejsce =1 and r.system like 'ligowy'
Select * from #mistrz
In Oracle SQL Data Modeler
The syntax for a procedure declaration is
CREATE OR REPLACE PROCEDURE IL_KLUB AS
vSUM_KLUBY KLUBY_ROZ_KLUB%TYPE;
vNAZWA ROZGRYWKI_KNUBOWE.NAZWA%TYPE;
BEGIN
select sum(krk.kluby_id),
r.nazwa
INTO vSUM_KLUBY,
vNAZWA
FROM rozgrywki_klubowe r,
kluby_roz_klub krk
WHERE r.id = krk.rozgrywki_klubowe_id
GROUP by r.nazwa;
END IL_KLUB;
This procedure is, obviously, not particularly useful as it doesn't do anything with the results of the SELECT, and that will probably draw a compilation warning. But that's how you declare it.
A function is defined in similar fashion, but you also need to define a return type, and then return a value:
CREATE OR REPLACE FUNCTION IL_KLUB_NAZWA
RETURN ROZGRYWKI_KNUBOWE.NAZWA%TYPE
AS
vSUM_KLUBY KLUBY_ROZ_KLUB%TYPE;
vNAZWA ROZGRYWKI_KNUBOWE.NAZWA%TYPE;
BEGIN
select sum(krk.kluby_id),
r.nazwa
INTO vSUM_KLUBY,
vNAZWA
FROM rozgrywki_klubowe r,
kluby_roz_klub krk
WHERE r.id = krk.rozgrywki_klubowe_id
GROUP by r.nazwa;
RETURN vNAZWA;
END IL_KLUB_NAZWA;
Oracle PL/SQL Language Reference here
Oracle SQL Language Reference here
Best of luck.

Resources