Virtual Private Database policy predicate has error - sql

I'm trying to implement a vpd. So far I have created a function:
> CREATE OR REPLACE FUNCTION sales_select(
schema_var IN VARCHAR2,
table_var IN VARCHAR2
)
RETURN VARCHAR2
IS
return_val VARCHAR2(400);
BEGIN
return_val := 'time_id >= "01-JAN-01"';
RETURN return_val;
END sales_select;
/
and the policy I made is the following:
L> BEGIN
2 DBMS_RLS.ADD_POLICY (
3 object_schema => 'sh',
4 object_name => 'costs1',
5 policy_name => 'costs_policy',
6 function_schema => 'policy_admin',
7 policy_function => 'sales_select',
8 statement_types => 'select'
9 );
0 END;
1 /
when I run the follow query:
select * from sh.costs1;
I get the following error:
ERROR at line 1:
ORA-28113: policy predicate has error
I'm thinking it has something to do with the quotes in the function but when I try changing them I get compile errors.

Is time_id a date column? If so, try changing line 9 to:
return_val := 'time_id >= date ''2001-01-01''';
Note the double quotes.

Related

SQL script, how can i execute the following script?

I have to execute this script . The problem in that in SQL Developer i cant run it,since some operators are not recognized .
P_jobgroup_id :
'GDWH2MYGDWH-GDWH2MYGDWH' CORE load finished
'GDWH2MYGDWH-GDWH2MYGDWH_IF' IF finished
'GDWH2MYGDWH-GDWH2MYGDWH_INIT' IF or CORE started
declare
p_context_code varchar2(100) := 'GDWH2MYGDWH';
v_res varchar2(100);
begin
v_res := OJC.jc_master.main
(
p_batch_number => NULL,
p_jobgroup_id => 'GDWH2MYGDWH-GDWH2MYGDWH'),
p_parameters_string => 'GDWH2MYGDWH.PRM_REPORTING_DATE='||to_char(sysdate,'yyyymmddhh24miss',
p_context_code =>p_context_code
);
end;
/
Error starting at line 1 in command: P_jobgroup_id : Error report:
Unknown Command
Error starting at line 2 in command: 'GDWH2MYGDWH-GDWH2MYGDWH'
CORE load finished Error report: Unknown Command ORA-06550: line 8,
column 50: PLS-00103: Encountered the symbol "," when expecting one of
the following:
. ( * % & = - + ; < / > at in is mod remainder not rem
The first par of the script is not valid PLSQL code.
Commenting or removing it will make the script run, once fixed a couple of syntax errors.
/*
P_jobgroup_id :
'GDWH2MYGDWH-GDWH2MYGDWH' CORE load finished
'GDWH2MYGDWH-GDWH2MYGDWH_IF' IF finished
'GDWH2MYGDWH-GDWH2MYGDWH_INIT' IF or CORE started
*/
DECLARE
p_context_code VARCHAR2(100) := 'GDWH2MYGDWH';
v_res VARCHAR2(100);
BEGIN
v_res := OJC.jc_master.main
(
p_batch_number => NULL,
p_jobgroup_id => 'GDWH2MYGDWH-GDWH2MYGDWH',
p_parameters_string => 'GDWH2MYGDWH.PRM_REPORTING_DATE=' || TO_CHAR(SYSDATE, 'yyyymmddhh24miss'),
p_context_code => p_context_code
);
END;
/

Invalid Identifier When Trying to Use Package Function in Summation Expression

I'm trying to use the sum function with a package function but running into an "invalid identifier" bug. Here's some example code with the error causing function commented
create or replace type numType as object
(
myNum number
)
;
/
create or replace type numTypes is table of numType;
/
create or replace package testNumberPackage as
function ReturnNum(in_numType numType) return number;
end;
/
create or replace package body testNumberPackage as
function ReturnNum(in_numType numType) return number is
begin
return in_numType.myNum;
end;
end;
/
declare l_numTypes numTypes;
l_count number;
begin
l_numTypes := numTypes();
for i in 1 .. 100 loop
l_numTypes.extend(1);
l_numTypes(l_numTypes.last) := numType(i);
end loop;
select sum(n.myNum) into l_count from table(l_numTypes) n;
select sum(testNumberPackage.ReturnNum(n)) into l_count from table(l_numTypes) n; --causes the error
dbms_output.put_line(l_count);
end;
/
The exact error for this code is
ORA-06550: line 11, column 42
PL/SQL: ORA-00904: "N": invalid identifier
ORA-6550: line 11, column 3:
PL/SQL: SQL Statement ignored
Thanks for any help.
The first issue is that you can't pass a table into a parameter by using its alias. It doesn't even make sense to try doing that.
The next issue is how to get the column mynum that is returned from the table(l_numTypes) into the correct format to pass into testNumberPackage.ReturnNum, since it's of NUMBER datatype, and the function is expecting a numtype parameter.
To do that, you need to pass in an object with that column, like so: numtype(n.mynum).
The following works for me:
declare
l_numTypes numTypes;
l_count number;
begin
l_numTypes := numTypes();
for i in 1 .. 100 loop
l_numTypes.extend(1);
l_numTypes(l_numTypes.last) := numType(i);
end loop;
select sum(n.myNum) into l_count from table(l_numTypes) n;
select sum(testNumberPackage.ReturnNum(numtype(n.mynum))) into l_count from table(l_numTypes) n; --causes the error
dbms_output.put_line(l_count);
end;
/
5050
Clear as mud?

Replacing text in a BLOB Column

In one of our tables we have a HUGEBLOB Column (Column name is DYNAMIC_DATA) which holding an XML data. What I need to do is updating a certain part of the text from within this BLOB.
I've tried this query:
UPDATE ape1_item_version
SET DYNAMIC_DATA = REPLACE (DYNAMIC_DATA,'Single period','Single period period set1')
WHERE name = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2'
But I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
How can I execute REPLACE on the BLOB ?
REPLACE works on the following datatypes:
Both search_string and replacement_string, as well as char, can be any of the data types CHAR, VARCHAR2, NCHAR, NVARCHAR2, CLOB, or NCLOB.
You have chosen to store character data as a collection of bytes (BLOB). These can not be worked on directly because a BLOB has no context and is only a very very big number. It can't be converted to characters without your input: you need its character set to convert binary data to text.
You'll have to either code the function REPLACE yourself (using DBMS_LOB.instr for instance) or convert your data to a workable CLOB and use standard functions on the CLOB.
I would advise strongly to change the datatype of your column. This will prevent any further character set conversion error you will likely run into in the future.
If you really want to work with blobs, use functions like these:
SQL> CREATE OR REPLACE FUNCTION convert_to_clob(l_blob BLOB) RETURN CLOB IS
2 l_clob CLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_clob, TRUE);
9 dbms_lob.converttoclob(dest_lob => l_clob,
10 src_blob => l_blob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_clob;
18 END convert_to_clob;
19 /
Function created
SQL> CREATE OR REPLACE FUNCTION convert_to_blob(l_clob CLOB) RETURN BLOB IS
2 l_blob BLOB;
3 l_dest_offset NUMBER := 1;
4 l_src_offset NUMBER := 1;
5 l_lang_context NUMBER := dbms_lob.default_lang_ctx;
6 l_warning NUMBER;
7 BEGIN
8 dbms_lob.createtemporary(l_blob, TRUE);
9 dbms_lob.converttoblob(dest_lob => l_blob,
10 src_clob => l_clob,
11 amount => dbms_lob.lobmaxsize,
12 dest_offset => l_dest_offset,
13 src_offset => l_src_offset,
14 blob_csid => nls_charset_id('AL32UTF8'),
15 lang_context => l_lang_context,
16 warning => l_warning);
17 RETURN l_blob;
18 END convert_to_blob;
19 /
Function created
You can call these functions directly from SQL:
SQL> UPDATE ape1_item_version
2 SET DYNAMIC_DATA = convert_to_blob(
3 REPLACE(convert_to_clob(DYNAMIC_DATA),
4 'Single period',
5 'Single period period set1')
6 )
7 WHERE NAME = 'PRIT ALL POOL for Duration Telephony 10_NA_G_V_H_N_Z2';
1 row updated
We can use something like the below query also with Oracle 11 and above if the blob object is of text.
`UPDATE table_name
SET text_blob_column-name = UTL_RAW.CAST_TO_RAW(
REPLACE(UTL_RAW.CAST_TO_VARCHAR2(text_blob_column-name),
'<existing value>',
'<value to update>')
)
WHERE where_clause_Column-name='171';`

COUNT function for files?

Is it possible to use COUNT in some way that will give me the number of tuples that are in a .sql file? I tried using it in a query with the file name like this:
SELECT COUNT(*) FROM #q65b;
It tells me that the table is invalid, which I understand because it isn't a table, q65b is a file with a query saved in it. I'm trying to compare the number of rows in q65b to a view that I have created. Is this possible or do I just have to run the query and check the number of rows at the bottom?
Thanks
You can do this in SQL*Plus. For example:
Create the text file, containing the query (note: no semicolon!):
select * from dual
Save it in a file, e.g. myqueryfile.txt, to the folder accessible from your SQL*Plus session.
You can now call this from within another SQL query - but make sure the # as at the start of a line, e.g.:
SQL> select * from (
2 #myqueryfile.txt
3 );
D
-
X
I don't personally use this feature much, however.
Here is one approach. It's a function which reads a file in a directory, wraps the contents in a select count(*) from ( .... ) construct and executes the resultant statement.
1 create or replace function get_cnt
2 ( p_file in varchar2 )
3 return number
4 as
5 n pls_integer;
6 stmt varchar2(32767);
7 f_line varchar2(255);
8 fh utl_file.file_type;
9 begin
10 stmt := 'select count(*) from (';
11 fh := utl_file.fopen('SQL_SCRIPTS', p_file, 'R');
12 loop
13 utl_file.get_line(fh, f_line );
14 if f_line is null then exit;
15 elsif f_line = '/' then exit;
16 else stmt := stmt ||chr(10)||f_line;
17 end if;
18 end loop;
19 stmt := stmt || ')';
20 execute immediate stmt into n;
21 return n;
22* end get_cnt;
SQL>
Here is the contents of a sql file:
select * from emp
/
~
~
~
"scripts/q_emp.sql" 3L, 21C
And here is how the script runs:
SQL> select get_cnt ('q_emp.sql') from dual
2 /
GET_CNT('Q_EMP.SQL')
--------------------
14
SQL>
So it works. Obviously what I have posted is just a proof of concept. You will need to include lots of error handling for the UTL_FILE aspects - it's a package which can throw lots of exceptions - and probably some safety checking of the script that gets passed.

Have PL/SQL Outputs in Real Time

Is it possible to have Outputs from PL/SQL in real time? I have a pretty huge package that runs for more than an hour and I'd like to see where the package is at a particular time.
Anyways, I currently do this with a log table, which gets filled up with hundreds of log descriptions per run, I'm just curious if this is possible.
Thanks!
This is the kind of thing I use (output can be seen in v$session and v$session_longops)...
DECLARE
lv_module_name VARCHAR2(48);
lv_action_name VARCHAR2(32);
gc_MODULE CONSTANT VARCHAR2(48) := 'MY_PROC';
-- For LONGOPS
lv_rindex BINARY_INTEGER;
lv_slno BINARY_INTEGER;
lc_OP_NAME CONSTANT VARCHAR2(64) := '['||gc_MODULE||']';
lv_sofar NUMBER;
-- This is a guess as to the amount of work we will do
lv_totalwork NUMBER;
lc_TARGET_DESC CONSTANT VARCHAR2(64) := 'Tables';
lc_UNITS CONSTANT VARCHAR2(64) := 'Rows';
CURSOR tab_cur
IS
SELECT owner, table_name
FROM all_tables;
BEGIN
<<initialisation>>
BEGIN
-- To preserve the calling stack, read the current module and action
DBMS_APPLICATION_INFO.READ_MODULE( module_name => lv_module_name
, action_name => lv_action_name );
-- Set our current module and action
DBMS_APPLICATION_INFO.SET_MODULE( module_name => gc_MODULE
, action_name => NULL );
END initialisation;
<<main>>
BEGIN
DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 01' );
NULL;
DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 02' );
FOR tab_rec IN tab_cur
LOOP
DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );
NULL;
END LOOP;
DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 03' );
--Initialising longops
lv_rindex := DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS_NOHINT;
lv_sofar := 0;
lv_totalwork := 5000; -- This is a guess, but could be actual if the query is quick
FOR tab_rec IN tab_cur
LOOP
DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );
lv_sofar := lv_sofar + 1;
-- Update our totalwork guess
IF lv_sofar > lv_totalwork
THEN
lv_totalwork := lv_totalwork + 500;
END IF;
DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex => lv_rindex
, slno => lv_slno
, op_name => lc_OP_NAME
, sofar => lv_sofar
, totalwork => lv_totalwork
, target_desc => lc_TARGET_DESC
, units => lc_UNITS
);
END LOOP;
-- Clean up longops
DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex => lv_rindex
, slno => lv_slno
, op_name => lc_OP_NAME
, sofar => lv_sofar
, totalwork => lv_sofar
, target_desc => lc_TARGET_DESC
, units => lc_UNITS
);
END main;
<<finalisation>>
BEGIN
-- Reset the module and action to the values that may have called us
DBMS_APPLICATION_INFO.SET_MODULE( module_name => lv_module_name
, action_name => lv_action_name );
-- Clear the client info, preventing any inter process confusion for anyone looking at it
DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => NULL );
END finalisation;
END;
/
I don't know if this is exactly what you want but I use dbms_application_info.set_module to see where my package is.
dbms_application_info.set_module(module_name => 'Conversion job',
action_name => 'updating table_x');
A query on v$session will show you which part of the procedure is running.
you could use autonomous transactions (as suggested in this SO for example).
This would allow you to write and commit in a log table without commiting the main transaction. You would then be able to follow what happens in your main script while it is running (incidentally, it will also allow you to time/tune your batch).
Use DBMS_PIPE to write a message to a named pipe. In another session you can read the messages from the pipe. Very simple, works like a charm !
procedure sendmessage(p_pipename varchar2
,p_message varchar2) is
s number(15);
begin
begin
sys.dbms_pipe.pack_message(p_message);
exception
when others then
sys.dbms_pipe.reset_buffer;
end;
s := sys.dbms_pipe.send_message(p_pipename, 0);
if s = 1
then
sys.dbms_pipe.purge(p_pipename);
end if;
end;
function receivemessage(p_pipename varchar2
,p_timeout integer) return varchar2 is
n number(15);
chr varchar2(200);
begin
n := sys.dbms_pipe.receive_message(p_pipename, p_timeout);
if n = 1
then
return null;
end if;
sys.dbms_pipe.unpack_message(chr);
return(chr);
end;
If your long-running job is processing a large number of fairly evenly sized tasks, you may find session longops a good way of monitoring the job progress, as well as allowing you to estimate how long the job will take to finish.
DBMS_APPLICATION_INFO.set_session_longops
If you have access to shell from PL/SQL environment you can call netcat:
BEGIN RUN_SHELL('echo "'||v_msg||'" | nc '||v_host||' '||v_port||' -w 5'); END;
/
v_host is a host running python script that reads data from socket on port v_port.
I used this design when I wrote aplogr for shell and pl/sql logs monitoring.