Use v('APP_USER') as default value for column in Oracle Apex - sql

I am trying to use v('APP_USER') as default value for a column. I get null when I use it in select like
select v('APP_USER') from dual;
But when I use it as default in column, like below, I am getting error.
create table test_table (col_1 varchar2(50) default NVL(v('APP_USER'), SYS_CONTEXT('USERENV','OS_USER')));
Error
create table test_table (col_1 varchar2(50) default NVL(v('APP_USER'), SYS_CONTEXT('USERENV','OS_USER')))
Error report -
ORA-04044: procedure, function, package, or type is not allowed here
04044. 00000 - "procedure, function, package, or type is not allowed here"
*Cause: A procedure, function, or package was specified in an
inappropriate place in a statement.
*Action: Make sure the name is correct or remove it.
Can anyone explain this or have a turnaround for this ??

There are other options than V('APP_USER'). Since Apex 5, the APP_USER is stored in the sys_context and that is a lot more performant than the V() function. It is available as SYS_CONTEXT('APEX$SESSION','APP_USER').
It also works as a default value for tables:
create table test_table
(col_1 VARCHAR2(100) DEFAULT SYS_CONTEXT('APEX$SESSION','APP_USER'));
Table TEST_TABLE created.
That being said, the best practice for audit columns is a trigger that populates the the 4 audit columns (as #Littlefoot suggested). Have a look at quicksql (under SQL Workshop > Utilities or on livesql.oracle.com). You can have it generate the triggers for you if you set "include Audit columns" and "Apex Enabled". An example of such a generated trigger is:
create or replace trigger employees_biu
before insert or update
on employees
for each row
begin
if inserting then
:new.created := sysdate;
:new.created_by := nvl(sys_context('APEX$SESSION','APP_USER'),user);
end if;
:new.updated := sysdate;
:new.updated_by := nvl(sys_context('APEX$SESSION','APP_USER'),user);
end employees_biu;
/

One option is to use a database trigger, e.g.
CREATE OR REPLACE TRIGGER trg_biu_test
BEFORE INSERT OR UPDATE ON test
FOR EACH ROW
BEGIN
:new.col1 := v ('APP_USER');
END;
/

Related

Creating a trigger with SELECT Function

Good day everyone,
I'm having trouble making my trigger work. As far as the functionality of the body and how it behaves, it does exactly as I intended for it to behave. However, when I start to fire the trigger, it returns an error in which Triggers should not have a SELECT statement from inside the main body. I'm still fairly new to coding and how to materialize the idea in my head into code. Hopefully someone could point me in a right direction on how change the Trigger I would like to have as a final result. Please see below script.
Update: Expected result would be whenever a user UPDATE a row and INSERT a new record via the application or job being run in the background, S1_HOVER_REPORT column would be updated with the value from the SELECT script and use the data from the S1_HOVER case result.
(Edit: I have updated the details of the problem above, added the Table being used and Error return)
Table: SITE
Column Name Type
------------------------------
ID VARCHAR2(14)
NAME VARCHAR2(70)
TYPE_CODE VARCHAR2(2)
PARENT VARCHAR2(14)
S1_HOVER_REPORT VARCHAR2(14)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
BEGIN
UPDATE (SELECT S1.ID,
S1.NAME,
S1.TYPE_CODE,
S1.PARENT AS PARENT1,
S2.PARENT AS PARENT2,
S1.S1_HOVER_REPORT,
CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE S1.ID
END AS S1_HOVER
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT) S3
SET S3.S1_HOVER_REPORT = S3.S1_HOVER;
END;
Error returned when Trigger fired:
Error report -
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
ORA-06512: at "MES.S1_HOVER_REPORT", line 2
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
(Update: I have included the updated trigger and it's now compiling without any issue, but I'm having errors whenever I try updating a record)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
BEFORE INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
DECLARE
v_S1_HOVER_REPORT VARCHAR2(14);
BEGIN
SELECT CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE (S1.ID)
END AS S1_HOVER
INTO v_S1_HOVER_REPORT
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT;
:NEW.S1_HOVER_REPORT := v_S1_HOVER_REPORT;
END;
Error report -
SQL Error: ORA-04091: table MES.SITE is mutating, trigger/function may not see it
ORA-06512: at "MES.S1_HOVER_REPORT", line 4
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Firstly from the error message
An attempt was made to insert or update columns of a join view which map to a non-key-preserved table.
S3 is the veiw ( you are creating the view by doing a select inside an update statment). You can try and change this to have key preservation but I really wouldn't know how.
The error suggests updating the base tables not the view. So as mentioned in the comments :old and :new are your friend.
:OLD holds all the values of the table the trigger is created on BEFORE the update (null if insert)
:NEW holds all the values of the table the trigger is created on AFTER the update / insert.....
So if I understand what you want to do correctly you would need to...
declare a variable eg v_S1_hover_report
do your select returning whatever value you need into this variable
set the value in your site table by doing
:NEW.S1_HOVER_REPORT := v_S1_hover_report
By setting this value into the :NEW object when a commit happens it will be committed to the database. This completely removes the need for an update statement in the trigger.
You can also use :NEW.id in your select statement to filter it down to the record you are updating if it is helpfull
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into v_test from dual;
:new.s1_hover_report := v_test;
END;
OR
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into :new.s1_hover_report from dual;
END;

Oracle Trigger error "ORA-00942: table or view does not exist"

this is my trigger :
create trigger tampone_trigger
after insert on tamponi.numerotelpaziente
for each row
begin
IF ( :new.numerotelpaziente not in (
select numtel
from users))
then
insert into spaiati values (new.numtel);
end if;
end;
The table "Tamponi" does exists, and "numerotelpaziente" is one of the columns...
Table "USERS" also exists, and "numtel" is one of its columns...
Why on earth is giving me that one error?
The trigger is supposed to look for this new cellPhone number inserted into "Tamponi" and check if this number exists in "Users", if not it has to be add to the separated table "spaiati" , where there is a column for it..
It is perfectly connected to my personal database (i'm running my JAVAfx application onto it, and it works fine, i just need to create some triggers).
Report error -
ORA-00942: table or view does not exist
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
If i use "on Tamponi" instead of giving the column aswell, the error becomes this one :
Report error -
ORA-04082: NEW or OLD references not allowed in table level triggers
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
if i use "on tamponi" the error is now this :
2/5 PL/SQL: Statement ignored
2/40 PLS-00405: subquery not allowed in this context
Errori: controllare il log del compilatore
Since this code will be in a trigger, you will want it to be as efficient as possible since it might be run very often. The code below should do what you are hoping to achieve with minimal context switching.
CREATE TRIGGER tampone_trigger
AFTER INSERT
ON tamponi
FOR EACH ROW
BEGIN
INSERT INTO spaiati
SELECT :new.numerotelpaziente
FROM DUAL
WHERE NOT EXISTS
(SELECT 1
FROM users u
WHERE u.numtel = :new.numerotelpaziente);
END;
/
You can try this -
create trigger tampone_trigger
after insert on tamponi
for each row
declare
v_flag boolean := false;
begin
for c in (select numtel from users)
loop
if :new.numerotelpaziente = c.numtel
then
v_flag := true;
exit;
end if;
exit when no_data_found;
end loop;
insert into spaiati values (new.numtel);
end;
I don't have a DB client available currently, but this should be close to what you want:
create trigger tampone_trigger
after insert on tamponi for each row
declare
v_exists number;
begin
select count(*) into v_exists from users u where u.numtel = :new.numerotelpaziente;
if (v_exists = 0) then
insert into spaiati values(:new:numerotelpaziente);
end if;
end;

Can't seem to create a trigger for insert -Oracle

I am trying to create a trigger on a table for an insert. Here is the creation script:
CREATE OR REPLACE TRIGGER INTEG_QRY_NEW
AFTER INSERT
ON INTEG_LOG
FOR EACH ROW
DECLARE
VAR2 NUMBER(10);
LOG_TEXT1 VARCHAR2(1000);
RESULT1 VARCHAR2(1000);
QUERY_NUM1 VARCHAR2(1000);
QUERY_TIME1 VARCHAR2(1000);
LOG_INDEX1 NUMBER(10);
BEGIN
SELECT COUNT(*) INTO VAR2 FROM USER_TAB_COLS WHERE (COLUMN_NAME = 'RESULT' OR COLUMN_NAME = 'QUERY_NUM' OR COLUMN_NAME = 'DATE_TIME' ) AND TABLE_NAME = 'INTEG_LOG';
IF VAR2=0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE INTEG_LOG ADD RESULT VARCHAR2(50); ALTER TABLE INTEG_LOG ADD DATE_TIME DATE; ALTER TABLE INTEG_LOG ADD QUERY_NUM VARCHAR2(50);';
END IF;
LOG_TEXT1 := :NEW.LOG_TEXT;
QUERY_TIME1 := :NEW.QUERY_TIME;
LOG_INDEX1 := :NEW.LOG_INDEX;
IF QUERY_TIME1 = '' THEN
RESULT1 := '-1';
QUERY_NUM1 := '';
UPDATE INTEG_LOG
SET RESULT = RESULT1,
DATE_TIME = CURRENT_DATE,
QUERY_NUM = QUERY_NUM1
WHERE LOG_INDEX = LOG_INDEX1;
ELSE
RESULT1 := SUBSTR(LOG_TEXT1,(INSTR(LOG_TEXT1,'Result = ')+9),9);
QUERY_NUM1 := SUBSTR(SUBSTR(LOG_TEXT1,INSTR(LOG_TEXT1,'Q# ')+3,20),1,INSTR(SUBSTR(LOG_TEXT1,INSTR(LOG_TEXT1,'Q# ')+3,20),' '));
UPDATE INTEG_LOG
SET RESULT = RESULT1,
DATE_TIME = CURRENT_DATE,
QUERY_NUM = QUERY_NUM1
WHERE LOG_INDEX = LOG_INDEX1;
END IF;
END;
/
The trigger is taking care of a regular insert for the following columns (that are coming from an application):
LOG_DATE NUMBER(10),
LOG_TIME NUMBER(10),
LOG_TYPE NUMBER(10),
LOG_TEXT VARCHAR2(2000 BYTE),
LOG_INDEX NUMBER(10),
QUERY_TIME VARCHAR2(8 BYTE)
the trigger checks for the following column to whether they exist or not (if not, it adds them):
RESULT VARCHAR2(50 BYTE),
DATE_TIME DATE,
QUERY_NUM VARCHAR2(50 BYTE)
Now it is taking the string (LOG_TEXT column), and withdraws the result value and query number (what comes after '#Q ').
Next, it should update the same insert opperation, and just adds these values to the new 3 columns (result,date_time and query_num).
The trigger does get created successfully but the problem is, when i am trying to insert into the table (INTEG_LOG), I get the following error:
table string.string is mutating, trigger/function may not see it
Cause: A trigger (or a user defined plsql function that is referenced
in this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
Action: Rewrite the trigger (or function) so it does not read that
table.
Might this be a syntax error ? Please help. Thank you.
The only reason the trigger compiles is because INTEG_LOG already has the columns you're dynamic SQL is trying to add. If the table didn't have those columns the trigger wouldn't add them because the trigger would be invalid as those UPDATE statements wouldn't compile.
One of the reasons why dynamic SQL should be avoid is that it turns compilation errors into runtime errors.
But trying to add columns to a table in a trigger built on that table is an astonishingly bad idea. Apart from anything else, DDL statement issue implicit commits and we cannot commit in triggers because that would play havoc with the transactions. (yes there is pragma autonomous_transaction but using that is usually a code smell).
The correct solution is:
Write a one-off DDL script to add those columns
If necessary make the script idempotent by checking for the prior existence of those columns before executing the ALTER TABLE statements
Run the script.
Populate the table with all the columns.
Of course this has nothing to do with the mutating table error which is due to those UPDATE statements. We cannot execute DML (including selects) on the table which hosts the trigger. I'm not sure what business rule you are trying to implement but you need a different way of doing it.

Oracle- Trigger Compiliation error

I am trying to create a trigger to check the month before inserting to the database. the followingg code was triedbut showing a complationng error as Warning: Trigger created with compilation errors.
this is the code
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
SELECT EXTRACT(month FROM EVN_DATE) FROM TBL_EVENT;
IF EXTRACT (month from EVN_DATE) == 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
/
I think you are looking for something like this:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END; /
Study the documentation, a section "Accessing Column Values in Row Triggers"
https://docs.oracle.com/cd/B19306_01/appdev.102/b14251/adfns_triggers.htm
to learn how to access columns of the current row within the trigger body.
In short: You need to use "correlation names" named NEW and OLD
Tip: run SET DEFINE OFF; before compiling the trigger to avoid bind variable substitution (variables prepended by a colon :).
Use it as:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
Declare
E_date date;
BEGIN
SELECT EVN_DATE into E_date FROM TBL_EVENT;
IF EXTRACT (month from E_date) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
Note: Oracle if clause require = only once
There are multiple isues with this code.
First it is a before insert trigger, so the value you want to check is not yet in the table. You can't find it with a select.
Triggers are pl/sql code. Any value you select within a pl/sql procedure you have to 'select aaa INTO bbb from xxx; and bbb must be declared before your BEGIN.
In Oracle the equal comparison operator is a single = (not ==).
Within a trigger you have the special qualifiers :new and :old to reference the column values you are working on.
In update triggers only the :new qualifier is usable.
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;

PLS-00103, in Trigger

create or replace TRIGGER log_worlds BEFORE UPDATE OR INSERT ON worlds
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD) VALUES(SYSDATE,ac,tab,world);
INSERT INTO worlds(WORLD_NAME) VALUES(world);
END;
Can someone help me with this,error information is about line 14? This trigger is supposed to add new values to the log table and to change the primary key value of the worlds table when my APEX application issues DML against the table.
the error you get suggests some syntax error in your code which I can't immediately find. However I would expect another error becuase Oracle won't allow you do execute DML statements (select, insert, update, delete) inside of a trigger on the same table as the trigger is on. Your trigger is on table worlds, so you are not allowed to insert a record into table worlds inside the trigger.
I think it's this line here:
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
If you replace it with:
world := world||to_char(NEW_WORLD.NEXTVAL);
it should work.
Btw: directly using a nextval call in an assignment does not work on versions prior to 11.x (not sure about the value of x here).
In 10.x you would need to declare a variable and then use:
SELECT to_char(new_world.nextval)
into world_num;
world := world || world_num;
The immediate syntax error is that assuming NEW_WORLD is a sequence you've created, you would need to do something like
SELECT world ||
cast( new_world.nextval as varchar2(10) )
INTO world
FROM dual;
rather than directly referencing the sequence in your CAST.
It's far from clear to me what your trigger is supposed to be doing though. It is, at a minimum, going to generate an infinite loop. Every INSERT on WORLDS is going to cause the trigger to fire which will generate an INSERT on WORLDS which will cause the trigger to fire, etc. Oracle will eventually raise an error when you exceed the maximum recursion depth. Perhaps you intended this to be a row-level trigger rather than a statement-level trigger that changed the value of :new.world_name? If that's the case, you'd probably want something like
create or replace TRIGGER log_worlds
BEFORE UPDATE OR INSERT ON worlds
FOR EACH ROW
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
select world || cast(new_world.nextval as varchar2(10)
into world
from dual;
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD)
VALUES(SYSDATE,ac,tab,world);
:new.world_name := world;
END;
This assumes that world_name is not actually the primary key. If world_name is the primary key then it would make no sense to modify the primary key value when a row is updated-- you would only want to potentially assign the primary key if you are doing an INSERT.