Intentionally Make the Database to Error - oracle

I need to know how to make Oracle database to error out. The reason is to try to catch an exception in the JDBC.
Calls to the database are invoked via web services that ultimately use JDBC calls to the database. So, consumers interface to the whole thing through SOAP requests. The majority of requests take xsd:string or/and xsd:date.
One way, for instance, I'm doing this is to set a character larger than 4000 character in the WHERE clause for a VARCHAR2 field of (BYTE 4000). Are there other ways of forcing the database to throw an error?

If you can't add objects to the database, then you'll need to use an error that can be created in pure SQL. Here are a few options:
Invalid date (ORA-01839): select to_date('2/30/2014','mm/dd/yyyy') from dual
Invalid number (ORA-01722): select cast('aaa' as number) from dual
Invalid math (ORA-01476): select 1/0 from dual
Invalid identifier (ORA-00904): select this_doesnt_exist('aaa') from dual
Invalid format (ORA-01810): select to_date('10','mmmmmmmmm') from dual

You could possibly do one of the following:
Use a procedure like A.B.Cade proposed;
Divide 1/0 or any other low-level error.
My option would be 1, with a few modifications:
create function raise_custom_error
( error_no number
, message varchar2
)
return number
as
begin
raise_application_error(error_no, message);
return 0;
end;
/
Use it like this:
select case
when 1=1 /*bad condition*/
then raise_custom_error(-20163, 'My custom error')
else 0
end
from dual

Related

Make a text field accept only numeric values in APEX

I have an APEX app that I am working on and need to make a field to only accept numeric values.
I want to have a validation that displays "Only numeric values allowed" whenever the user inputs letters or other characters that are not numeric.
How can I achieve this?
Thanks in advance
If you use the translate function, you can "detect" whether string contains something but digits (this example allows decimal dot as well):
SQL> with test (col) as
2 (select '1234' from dual union all -- valid
3 select '12a4' from dual union all -- invalid
4 select '12.4' from dual union all -- valid
5 select 'abcd' from dual union all -- invalid
6 select '1#34' from dual -- invalid
7 )
8 select col, translate(col, 'x0123456789.', 'x') result
9 from test;
COL RESU
---- ----
1234
12a4 a
12.4
abcd abcd
1#34 #
SQL>
So, if the result is NULL, then it is a valid value.
In Apex, you'd create validation (function that returns error text) which looks like this:
if translate(:P1_ITEM, 'x0123456789.', 'x') is not null then
return 'Only numeric values allowed';
else
return null;
end;
These field definitions are more about the inherent server-side validations that will run on your behalf to ensure the data submitted is numeric.
I think the best you're going to get is having some client side validation as well that gives the user immediate feedback - either has message, or auto-strip. I'm not sure if you can force a HTML element to only allow numerics.
If you really need to validate after each new character, you can try to add Dynamic Action based on Key Release event and Execute JavaScript code when true. Something like this
if ( $v("P2_NEW") !== ""
&& isNaN($v("P2_NEW"))) {
alert("Not a number");
}
Ok after some hours of trying this and that, the following is what has worked for me.
Click in the page. In the "Function and Global Variable Declaration" section add this code:
function isInputNumber(evt){
var ch = String.fromCharCode(evt.which);
if(!(/[0-9]/.test(ch))){
evt.preventDefault();
}
}
In the "Page HTML Body Attribute" section add this code:
onkeypress="isInputNumber(event)"
and thats it. This will prevent the user from entering letters into the input field.

Is there a hint to generate execution plan ignoring the existing one from shared pool?

Is there a hint to generate execution plan ignoring the existing one from the shared pool?
There is not a hint to create an execution plan that ignores plans in the shared pool. A more common way of phrasing this question is: how do I get Oracle to always perform a hard parse?
There are a few weird situations where this behavior is required. It would be helpful to fully explain your reason for needing this, as the solution varies depending why you need it.
Strange performance problem. Oracle performs some dynamic re-optimization of SQL statements after the first run, like adaptive cursor sharing and cardinality feedback. In the rare case when those features backfire you might want to disable them.
Dynamic query. You have a dynamic query that used Oracle data cartridge to fetch data in the parse step, but Oracle won't execute the parse step because the query looks static to Oracle.
Misunderstanding. Something has gone wrong and this is an XY problem.
Solutions
The simplest way to solve this problem are by using Thorsten Kettner's solution of changing the query each time.
If that's not an option, the second simplest solution is to flush the query from the shared pool, like this:
--This only works one node at a time.
begin
for statements in
(
select distinct address, hash_value
from gv$sql
where sql_id = '33t9pk44udr4x'
order by 1,2
) loop
sys.dbms_shared_pool.purge(statements.address||','||statements.hash_value, 'C');
end loop;
end;
/
If you have no control over the SQL, and need to fix the problem using a side-effect style solution, Jonathan Lewis and Randolf Geist have a solution using Virtual Private Database, that adds a unique predicate to each SQL statement on a specific table. You asked for something weird, here's a weird solution. Buckle up.
-- Create a random predicate for each query on a specific table.
create table hard_parse_test_rand as
select * from all_objects
where rownum <= 1000;
begin
dbms_stats.gather_table_stats(null, 'hard_parse_test_rand');
end;
/
create or replace package pkg_rls_force_hard_parse_rand is
function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2;
end pkg_rls_force_hard_parse_rand;
/
create or replace package body pkg_rls_force_hard_parse_rand is
function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2
is
s_predicate varchar2(100);
n_random pls_integer;
begin
n_random := round(dbms_random.value(1, 1000000));
-- s_predicate := '1 = 1';
s_predicate := to_char(n_random, 'TM') || ' = ' || to_char(n_random, 'TM');
-- s_predicate := 'object_type = ''TABLE''';
return s_predicate;
end force_hard_parse;
end pkg_rls_force_hard_parse_rand;
/
begin
DBMS_RLS.ADD_POLICY (USER, 'hard_parse_test_rand', 'hard_parse_policy', USER, 'pkg_rls_force_hard_parse_rand.force_hard_parse', 'select');
end;
/
alter system flush shared_pool;
You can see the hard-parsing in action by running the same query multiple times:
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
Now there are three entries in GV$SQL for each execution. There's some odd behavior in Virtual Private Database that parses the query multiple times, even though the final text looks the same.
select *
from gv$sql
where sql_text like '%hard_parse_test_rand%'
and sql_text not like '%quine%'
order by 1;
I think there is no hint indicating that Oracle shall find a new execution plan everytime it runs the query.
This is something we'd want for select * from mytable where is_active = :active, with is_active being 1 for very few rows and 0 for maybe billions of other rows. We'd want an index access for :active = 1 and a full table scan for :active = 0 then. Two different plans.
As far as I know, Oracle uses bind variable peeking in later versions, so with a look at the statistics it really comes up with different execution plans for different bind varibale content. But in older versions it did not, and thus we'd want some hint saying "make a new plan" there.
Oracle only re-used an execution plan for exactly the same query. It sufficed to add a mere blank to get a new plan. Hence a solution might be to generate the query everytime you want to run it with a random number included in a comment:
select /* 1234567 */ * from mytable where is_active = :active;
Or just don't use bind variables, if this is the problem you want to address:
select * from mytable where is_active = 0;
select * from mytable where is_active = 1;

Oracle. CAST COLLECT for date datatype

Consider the types:
CREATE OR REPLACE TYPE date_array AS TABLE OF DATE;
CREATE OR REPLACE TYPE number_array AS TABLE OF NUMBER;
CREATE OR REPLACE TYPE char_array AS TABLE OF VARCHAR2(80);
Queries:
WITH q AS
(SELECT LEVEL ID,
TRUNC(SYSDATE) + LEVEL MyDate,
to_char(LEVEL) STRING
FROM dual
CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(ID) AS number_array)
FROM q;
return collection of numbers
WITH q AS
(SELECT LEVEL ID,
TRUNC(SYSDATE) + LEVEL MyDate,
to_char(LEVEL) STRING
FROM dual
CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(STRING) AS char_array)
FROM q;
return collection of strings
WITH q AS
(SELECT LEVEL ID,
TRUNC(SYSDATE) + LEVEL MyDate,
to_char(LEVEL) STRING
FROM dual
CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(MyDate) AS date_array)
FROM q
return error invalid datatype.
Can anyone explain why the Date datatype behaves differently?
Here are my findings... it seems you are facing a bug caused by the fact that calculated dates seem to have a different internal representation from "database" dates. I did find a workaround, so keep on reading.
On my oracle dev installation (Oracle 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production) I am experiencing your same problem.
BUT... If I create a physical table containing your test data:
create table test_data as
SELECT LEVEL ID,
TRUNC(SYSDATE) + LEVEL MyDate,
to_char(LEVEL) STRING
FROM dual
CONNECT BY LEVEL < 5
and then run the "cast collect" operator on this physical table, it works as expected:
-- this one works perfectly
SELECT CAST(COLLECT(MyDate) AS date_array) from test_data
but all these examples still do NOT work:
-- here I just added 1 .. and it doesn't work
SELECT CAST(COLLECT(MyDate + 1) AS date_array)
from test_data
-- here I am extracting sysdate, instead of a physical column... and it doesn't work
SELECT CAST(COLLECT(sysdate) AS date_array)
from test_data
It feels like oracle doesn't think that calculated dates are the same thing of physical dates
So, I tried to "persuade" oracle that the data I am providing is actually a normal DATE value, using an explicit cast... and EUREKA! this does the job correctly:
WITH q AS
(SELECT LEVEL ID,
-- this apparently unnecessary cast does the trick
CAST( TRUNC(SYSDATE) + LEVEL AS DATE) MyDate,
to_char(LEVEL) STRING
FROM dual
CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(MyDate) AS date_array)
FROM q
Yes... but why??
It really seems that these two values are not exactly the same thing, even if the values we see are actually the same:
select sysdate, cast (sysdate as date) from dual
So I dug in the internal representation of the two values applying the "dump" function to both of them:
select dump(sysdate), dump(cast (sysdate as date)) from dual
and these are the results I got:
DUMP(SYSDATE ) -> Typ=13 Len=8: 226,7,11,9,19,20,47,0
DUMP(CAST(SYSDATEASDATE) as DUAL) -> Typ=12 Len=7: 120,118,11,9,20,21,48
Internally they look like two totally different data types! one is type 12 and the other is type 13... and they have different length and representation.
Anyway I discovered something more.. it seems someone else has noticed this: https://community.oracle.com/thread/4122627
The question has an answer pointing to this document: http://psoug.org/reference/datatypes.html
which contains a lengthy note about dates... an excerpt of it reads:
"What happened? Is the information above incorrect or does the DUMP()
function not handle DATE values? No, you have to look at the "Typ="
values to understand why we are seeing these results. ". The datatype
returned is 13 and not 12, the external DATE datatype. This occurs
because we rely on the TO_DATE function! External datatype 13 is an
internal c-structure whose length varies depending on how the
c-compiler represents the structure. Note that the "Len=" value is 8
and not 7. Type 13 is not a part of the published 3GL interfaces for
Oracle and is used for date calculations mainly within PL/SQL
operations. Note that the same result can be seen when DUMPing the
value SYSDATE."
Anyway, I repeat: I think this is a bug, but at least I found a workaround: use an explicit cast to DATE.

Can I write a function that behaves like REGEXP_LIKE, ie the result type of which behaves as a boolean?

In Oracle, usually, SQL functions that should return true/false return 1/0 because the BOOLEAN data type only exists in PL/SQL blocks.
For example with Oracle Text, you must not forget the > 0 in SELECT * FROM bartbl WHERE CONTAINS(foocol, 'sometext') > 0. Otherwise you get ORA-00920 invalid relational operator.
However, it seems that REGEXP_LIKE behaves like a true boolean: you can do SELECT * FROM bartbl WHERE REGEXP_LIKE(foocol, 'sometext') and it works.
So I would like to know why this is the case and if there is a possibility for me to write some functions like this?
Once upon a time, there was no boolean requirement in ANSI SQL (not sure if that requirement exists in latest standards?), and Oracle tends to do their own thing sometimes ;) But, personally I don't see the issue in using Y/N or 1/0.
Anyway, REGEXP_LIKE is really a type of filter (can reduce the number of rows returned), not the same as a typical function (which will return a value (or null) for each row it processes). But good thing is that if you really want to mimic the filter functionality, you can use REGEXP_LIKE with your own function. For example:
create or replace function is_big_number(i_num in number)
return varchar2
as
begin
if (i_num >= 1000000) then
return 'Y';
end if;
return 'N';
end;
with x as (
select 50000 as num from dual
union all
select 1000000 as num from dual
union all
select 3 as num from dual
)
select num, is_big_number(num)
from x
-- this is a filter, not a typical "function"
where
regexp_like(is_big_number(num), 'Y');
Output:
NUM IS_BIG_NUMBER(NUM)
1000000 "Y"
Hope that helps.
Oracle's built in functions are special, you can't write one like this yourself. I don't know why though

using sys_context to match data string

I'm trying to use data in sys_context form to perform a match in a WHERE clause.
What I put into the context is ('53','89'), which is what is returned when I select against dual.
My where statement is: where to_char(location_id) in sys_context('my_ctx','valoc')
Since I'm not getting the expected response, I'm guessing that what I think Oracle should see is not actually what it sees, but I don't know how to "look" at what's passed to the processor from TOAD.
The original form was where location_id in sys_context('my_ctx','valoc') with (53,89) in valoc, but that didn't return anything either. I'm sensing there may be no answer to my problem.
The problem is that the resulting WHERE clause is equivalent to this:
where to_char(location_id) in '('53','89')'
(didn't double the inner apostrophes for clarity)
The database sees what's retrieved from context as a single value, not as a list of values.
You can use the CONNECT BY trick to achieve your goal:
SELECT 1
FROM dual
WHERE '53' IN ( -- replace '53' with TO_CHAR(location_id)
SELECT regexp_substr('53,89', '[0-9]*', 1, level) -- replace '53,89' with sys_context('my_ctx','valoc')
FROM dual
CONNECT BY regexp_substr('53,89', '[0-9]*', 1, level) IS NOT NULL -- replace '53,89' with sys_context('my_ctx','valoc')
);

Resources