Every time I try to run my database script I reach the last few lines that have my 2 triggers and my script stops after compiling the first trigger.
These are the 2 triggers I have and it compiles "Player Round Trigger" and then the script stops and doesn't reach my second trigger "Handicap Trigger"
--
-- Player Round Trigger
--
CREATE TRIGGER playerRoundUpdateAudit BEFORE UPDATE ON PlayerRound
FOR EACH row BEGIN
INSERT INTO PlayerRoundAudit(old_data_PlayerID, old_data_RoundID, old_data_Holenumber, old_data_holeScore,
new_data_PlayerID, new_data_RoundID, new_data_Holenumber, new_data_holeScore, tbl_name)
VALUES (OLD.playerID, OLD.roundID, OLD.holeNumber, OLD.holeScore, NEW.playerID, NEW.roundID, NEW.holeNumber, NEW.holeScore, "PlayerRound");
END;
/
--
-- Handicap Trigger
--
CREATE TRIGGER handicapUpdateAudit BEFORE UPDATE ON Handicap
FOR EACH row BEGIN
INSERT INTO HandicapAudit(old_data_handicapID, old_data_playerID, old_data_handicapDate, old_data_handicapScore,
new_data_handicapID, new_data_playerID, new_data_handicapDate, new_data_handicapScore, tbl_name)
VALUES (OLD.handicapID, OLD.playerID, OLD.handicapDate, OLD.handicapScore, NEW.handicapID, NEW.playerID, NEW.handicapDate, NEW.handicapScore, "Handicap");
END;
/
I'm running the script in Oracle SQL Developer version 4.1.2.20 (the newest one atm)
Acturlly the first triggers compiles with errors, and breaks the script.
You can do an experiment - change a header of first trigger into CREATE OR REPLACE TRIGGER ....,
then in SQL Developer click on the first trigger to move cursor into it's code, then press Ctrl-Enter - this executes one statement where the cursor is placed (actually - 'CREATE` statement of the first trigger).
Then examine "Compiler log" window - you will see a message like this:
There are two problems with this trigger:
you are using OLD.column_name and NEW.column_name, what is wrong. You need use :OLD.column_name and :NEW.column_name, using a colon as a prefix
you are using double quotes instead of quotes here: "PlayerRound", and Oracle doesn't interpret this as a string, but as an identifier (of variable, column name etc.). Use 'PlayerRound' within quotes instead.
Change the first trigger like below an it should compile:
set define off
CREATE or replace TRIGGER playerRoundUpdateAudit BEFORE UPDATE ON PlayerRound
FOR EACH row BEGIN
INSERT INTO PlayerRoundAudit(old_data_PlayerID, old_data_RoundID,
old_data_Holenumber, old_data_holeScore,
new_data_PlayerID, new_data_RoundID, new_data_Holenumber,
new_data_holeScore, tbl_name)
VALUES (:OLD.playerID, :OLD.roundID, :OLD.holeNumber, :OLD.holeScore,
:NEW.playerID, :NEW.roundID, :NEW.holeNumber, :NEW.holeScore, 'PlayerRound');
end;
/
You need also correct the second trigger, because it contains similar errors.
Remark : put SET DEFINE OFF into the script to turn off a variable substitution, otherwise SQL-Developer will prompt to enter a value for each :NEW and :OLD
I'm not so familiar with Oracle SQL Developer but is there an option to run the script as "Execute as script"? This feature is available on TOAD...
Related
Total newbie in AL here; I'm experimenting with automated testing in Dynamics BC for a new deployment. As a very basic test, I'd like to simply create a new Item record in the Cronus test database and validate each field. I'm running into trouble when I try to select the value for an enum field. Here's the code I'm using:
Procedure AddGoodItem()
// [Given] Good item data
var
recItem: Record Item Temporary;
Begin
recItem."Description" := 'zzzz';
recItem.validate("Description");
recItem.Type := recItem.Type::Inventory;
recItem.Validate(Type);
recItem."Base Unit of Measure" := 'EA';
recItem.Validate("Base Unit of Measure");
recItem."Item Category Code" := 'FURNITURE';
recItem.validate("Item Category Code");
End;
When I run this in Cronus, I get the error:
You cannot change the Type field on Item because at least one Item Journal Line exists for this item.
If I comment the Type lines out, the process runs successfully.
Given that this is a temporary record, it shouldn't have any Item Journal Lines, should it? What am I missing?
The code in the OnValidate trigger still runs, even if you have marked the Record variable as temporary. In addition temporary is not inherited to the underlying variables meaning any Record variables used in the OnValidate trigger are not temporary (unless marked as such).
There are two options:
Leave out the line recItem.Validate(Type);, if the code run by the OnValidate trigger is not relevant in this case.
Replace the line recItem.Validate(Type); with your own clone of the code from the OnValidate trigger and then remove the unneeded parts.
I'm trying to copy the last inserted row from a table into a csv file using a trigger.
CREATE TRIGGER new_tbc_order
AFTER INSERT
ON trig_test
FOR EACH ROW
EXECUTE PROCEDURE write_last_tbc_order();
CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER
LANGUAGE plpgsql
as $$
BEGIN
EXECUTE 'COPY (
select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS i
) TO ''/Users/fred/Desktop/last_tbc_order.csv'' csv;' USING NEW;
RETURN NEW;
END; $$
I've tried this in various incarnations, with or without EXECUTE but I'm still getting the error.
ERROR: missing FROM-clause entry for table "new"
LINE 1: ...opy (select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS ...
^
Just cannot get it to access the NEW data.
Where am I going wrong ?
The only way I could get this to work is something like this:
CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER
LANGUAGE plpgsql
as $$
BEGIN
EXECUTE 'COPY (
select id, paid, no_items FROM trig_test WHERE id = ' || NEW.id ||
') TO ''/Users/fred/Desktop/last_tbc_order.csv'' csv;';
RETURN NEW;
END; $$
COPY does not seem to 'see' the NEW record. Be aware that the above will fail if the Postgres server does not have permissions on /Users/fred/Desktop/ as COPY runs as the server user. Personally I think a better solution is to write to a audit table and periodically harvest the records from there.
Adrian's answer inspired me to experiment more with the NEW record where it was actually available.
The actual answer, as usual, turned out to be simple-ish:
CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER
AS
$$
BEGIN
EXECUTE 'copy (select '''||NEW.id||''','''||NEW.paid||''','''||NEW.no_items||''')
to ''/Users/shaun/Desktop/last_tbc_order.csv'' csv;' USING NEW;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
Thus keeping the NEW record outside of the copy statement and building it dynamically before the execute.
This still required the USING NEW part at the end to make it work correctly.
Also required all the quoting.
UPDATE:
The above code does require almost everything to be running as a Postgres Superuser, (as mentioned by several commenters), which is not ideal.
To get around this, you can create a PlPython function as follows:
CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER AS
'
import os
row = str(TD["new"]["id"]) + "," + TD["new"]["paid"] + "," + str(TD["new"]["noitems"])
path = "/tmp/db-data/last_order.csv";
with open(path,"w") as o:
os.chmod(path, 0o644)
o.write(row)
return None
'
LANGUAGE 'plpythonu';
This function must be created as a Superuser, but it can be used in triggers created by standard users, and those triggers also work when inserts are fired by standard users.
The place to where the file is written must have write permission for the postgres user in every part of the path, so I created a subdirectory of /tmp with 777 permissions.
Also needed to add in the os chmod 644 statement to make the file readable by other users on the system. Without this, the file is created with permissions of 600.
FURTHER NOTES:
In the end Apache doesn't like you to setup a virtual directory inside the /tmp directory so in the end had to create another called /tmp-wh specifically for this purpose.
I'm trying to use db2 pl/sql script giving a template code.
--#set delimiter !
begin atomic
for S as
<query statement that finds quests, "swaps", with a donor/donation & recipient pair>
do
<update statement that fixes Loot with the swap 'S'>;
end for;
-- handle each swap
end!
-- we're done once through
in my query statement i used something like this:
with
t1 (args) as (
...
)
...
select ...
where ... ;
in the update statement
update Loot set ... where ...
but the problem is, when i try to run the full sql code script on the database, I keep getting the message :
"An unexpected token "begin" was found following "<identifier>".
Expected tokens may include: "USER". SQLSTATE=42601 DB21007E End of file
reached while reading the command.
I want to know, how to use the proper syntax or format to include the "with queries" and also update statement to stop giving me the error. I have the "with query" working in a separate file, but when i combine both statements into the template, it would give me this error. Also as well, if I were to include triggers, which part of the code should i put it in. Thank you.
Here is an example that might help.
CREATE TABLE EG(I INT, J INT)!
INSERT INTO EG VALUES (1,1),(2,3)!
begin
for c as with w(i,u) as(values (2,5)) select i,u from w
do
update eg set j = j + c.u where i = c.i;
end for;
end
!
If this does not help your problem, please post a version of your code that we can run and which returns the error message that you are struggling with.
I found this code to create a calc field to a TADOTable in Delphi somewhere ...
.....
procedure TfrmMain.ABSTable1CalcFields(DataSet: TDataSet);
begin
with ABSTable1 do
FieldByName('cost').AsFloat := FieldByName('price').AsFloat *
FieldByName('quantity').AsInteger;
// add new field cost as Price * quantity !!!!
end;
end.
Inside my app i create a TADOQuery at rum time like
try
Fquery.sql.clear;
Fquery.sql.AddStrings(Amemo.lines);
Fquery.Open;
.....
finally
end;
How to add more calc fields to my Query derived from the first code Fragment ?
I think the only way you can easily do this is by creating a set of persistent TFields in the IDE (or do the equivalent creation of them in code before you open the dataset). Otherwise, when you call Open on the dataset, IIRC that will call BindFields and that - unless the dataset already has a set of TFields - will create a set of dynamic TFields which will last as long as the dataset is open, but will not include any calculated fields.
By the time BindFields has been called, it's too late to add any more, so you have to set them up beforehand or not at all.
I'm working with Oracle Forms. I have a field named SOLD_TO_CUST_PARTY_NAME. I have to execute a process if I detect a change in the field's value. I tried using when_validate, but it's executed even if you just click the field and move to another field (validation ALWAYS occurs whether you change the value or not). Is there anyway I can check :old and :new or something similar to execute a process only if the field is modified?
EDIT:
Can't use personalizations. It has to be done with pl/sql.
There is a property called database value that let you check if the field has been modified and if it has not you just have to exit the validation trigger.
Ex.
BEGIN
IF :BLOCK.ITEM = GET_ITEM_PROPERTY('BLOCK.ITEM', database_value) THEN
RETURN;
END IF;
/* VALIDATION */
END;