Use new value in after trigger sql statement - sql

I am trying to create a trigger that will dump out a csv file of data at a certain point in an application. When you create a payment order from a payment proposal then it means it is ready to be paid and uploaded to the bank. There is a wizard in the erp that makes the payment order from the payment proposal. There is also a header and a detail table on both proposals and orders. I need it to make it when there is a new row in the payment order table that has new.way_id = 'ACH' and new.institute_id = 'BMO'.
The problem is the wizard has multiple steps and doesn't insert the detail rows for the order till the last step when you click ok, but the header is created already before this and executes the trigger. Because of this when the header is created I am going to pull all of the data from the proposal header and detail because it is all already there.
When The trigger executes it makes a sql statement and then passes it to a stored procedure that will take any sql query and dump it to a csv file. For some reason it won't let me use the new reference when I create my query. I get an error saying "new.selected_proposals invalid identifier". I need it to do a like on this also because you can select multiple proposal header id's when you create an order and I only want it to do it for the ones from the proposals that have a way_id of ACH.
I am guessing I have to add the .new table, whatever it is, into the join or something, but I am not sure how to do that.
This is Oracle database 11g. Here is the code. The commented out section in the query is what I am trying to fix, just to give an idea of what I am trying to do.
create or replace TRIGGER CREATE_BMO_ACH_FILE
AFTER INSERT ON PAYMENT_ORDER_TAB
for each row
when (new.way_id = 'ACH' and new.institute_id = 'BMO')
declare sql_ varchar(4000);
BEGIN
sql_ := q'[select pp.company, pp.Proposal_id, pp.CREATION_DATE, pl.identity, pl.payee_identity, pl.ledger_item_id, pl.currency, pl.curr_amount, pl.GROSS_PAYMENT_AMOUNT, pl.PLANED_PAYMENT_DATE, pl.Order_Reference, pl.PAYMENT_REFERENCE
from payment_proposal pp, PROPOSAL_LEDGER_ITEM pl
where pp.company = pl.company
and pp.proposal_id = pl.proposal_id
and pp.way_id = 'ACH'
/*and pp.proposal_id like '%' || new.selected_proposals || '%'*/]';
dump_sql_to_csv( sql_, 'E:\Accounting', 'test.csv');
END;

I think your just missing a colon in front of the new i.e. use :new.selected_proposals. Colons aren't required for old and new in the WHEN but are in the code block.

Related

what is the code to create a table in sql only at first run of project?

hello everyone,
I want to create a web application using jsp,servlet and jdbc etc.
It simply create a table of name student,contains various field as information about student in database and different page perform different query tasks .
The problem is that I want to create a war file to distribute this app to my client.
When I write a sql query to create a table in my jsp page , it tries to create a new table every time whenever I run this project.
Here i want that it creates a table only when the user run this first time.So that when I distribute the war file to my client ,on the first run it create a student name table and perform the required query and for the future run it only perform other query but not create a table again.
I need your valuable guidance and overview for solving this problem.Any type of help will be appreciated.
"thanks"
In Oracle, the workaround is to wrap it in an anonymous BEGIN-END block. The EXCEPTION block would simply allow the table already exists error.
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE...
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -955 THEN
NULL; -- ignore the ORA-00955 error
ELSE
RAISE;
END IF;
END;
/
This is one way preferably. Another way would be to manually check if the table exists or not in the data dictionary view dba_tables.
SELECT count(*)
INTO variable_count
FROM dba_tables
WHERE table_name = 'table_name';
So,
IF variable_count = 0 THEN EXECUTE IMMEDIATE CREATE TABLE ...

How to demonstrate SQL injection in where clause?

I want to demonstrate the insecurity of some webservices that we have. These send unsanitized user input to an Oracle database Select statements.
SQL injection on SELECT statements is possible (through the WHERE clause), however I am having a hard time to demonstrate it as the same parameter gets placed in other queries as well during the same webservice call.
E.g:
' or client_id = 999'--
will exploit the first query but as the same WS request calls runs other SQL SELECTs, it will return an oracle error on the next query because the client_id is referred to by an alias in the second table.
I am looking to find something more convincing rather than just having an ORA error returned such as managing to drop a table in the process. However I do not think this is possible from a Select statement.
Any ideas how I can cause some data to change, or maybe get sensitive data to be included as part of an ORA error?
It's not very easy to change data, but it's still possible. Function that created with pragma autonomous_transaction can contain dml and may be called in where. For instance,
CREATE OR REPLACE FUNCTION test_funct return int
IS
pragma autonomous_transaction;
BEGIN
DELETE FROM test_del;
commit;
return 0;
end;
-- and then
SELECT null from dual where test_funct()=1;
Another option you try creating huge subquery in WHERE which in turn may cause huge performance issue on server.
You do not need a custom function, you can use a sub-query:
" or client_id = (SELECT 999 FROM secret_table WHERE username = 'Admin' AND password_hash = '0123456789ABCD')"
If the query succeeds then you know that:
There is a table called secret_table that can be seen by the user executing this query (even if there is not a user interface that would typically be used to directly interact with that table);
That it has the columns username and password_hash;
That there is a user called Admin; and
That the admin user has a password that hashes to 0123456789ABCD.
You can repeat this and map the structure of the entire database and check for any values in the database.

Using the updated item in a trigger

I am trying to create an SQL trigger for when the quantity on hand (qoh) of an item in the Oracle 12c SQL database falls below 5. I want to select the description of that item from another table, and have come up with the following query, but I am getting an error when I try to run it:
/*Creates a trigger to notify someone when an item is out of stock*/
CREATE OR REPLACE TRIGGER ItemOutOfStock
AFTER UPDATE OF INV_QOH ON inventory
FOR EACH ROW
WHEN (new.INV_QOH < 5)
BEGIN
SELECT I.ITEM_DESC
FROM ITEMS I
WHERE I.ITEM_ID = new.ITEM_ID;
END;
/
From what I have been able to figure out I should be able to call new.item_id and it works, but that isn't the case. When I update the inventory table and set an item's quantity to less than 5 with just a dbms_output.put_line command it puts the text to the output, so I know the problem is somewhere in the select statement.
A SELECT statement has to do something with the data. Potentially, you could store the data in a local variable declared in your trigger. Something like
CREATE OR REPLACE TRIGGER ItemOutOfStock
AFTER UPDATE OF INV_QOH ON inventory
FOR EACH ROW
WHEN (new.INV_QOH < 5)
DECLARE
l_item_desc item.item_desc%type;
BEGIN
SELECT I.ITEM_DESC
INTO l_item_desc
FROM ITEMS I
WHERE I.ITEM_ID = :new.ITEM_ID;
<<do something with l_item_desc>>
END;
/
Note that the :new pseudo-record needs to be prefaced with a colon.
Be aware as well that sending an email from a trigger is generally a bad idea since sending an email is non-transactional. A trigger can be fired but the transaction can be rolled back so the email can get sent even though the update was never committed. A trigger can also be executed multiple times for a single change (with a rollback in between) because of write consistency. To do it correctly, you'd really want the trigger to do something like write to a table that a separate processes polls periodically in order to send emails or to submit a job (using dbms_job) that sends an email if and only if the underlying transaction commits.

Delphi update a joined query

The application is datasnap (with sqlite database). I run the following query against two tables (LOKACIJE and UPORABNIKI):
procedure TForm4.FormShow(Sender: TObject);
begin
ClientDataSet1.Close;
ClientDataSet1.CommandText :='';
ClientDataSet1.CommandText := 'select lokacije.[HOTEL_ID],'+
' lokacije.[NAZIV],'+
' uporabniki.[LOKACIJA_ID],'+
' uporabniki.[RESORT_ID],'+
' uporabniki.[HOTEL_ID],'+
' uporabniki.[UPORABNIK],'+
' uporabniki.[GESLO],'+
' uporabniki.[PRAVICE]'+
' from UPORABNIKI'+
' inner join LOKACIJE on uporabniki.lokacija_id=lokacije.lokacija_id '+
' where lokacije.[NAZIV] = :#NAZIV'+
' order by Uporabniki.[UPORABNIK]';
ClientDataSet1.Params.ParamByName('#NAZIV').Value:= '' + Form2.AdvOfficeStatusBar1.Panels[3].Text + '' ;
ClientDataSet1.Open;
end;
The query runs fine and gives me the desired results. However I would like to be able to edit and save the edited (or added) results of this query. The table that I want to update (or add the new record) is the UPORABNIKI. I do not need to write anything to the LOKACIJE table. How am I to accomplish this ?
Apart from saving new record I would like the query to fill automatically the values
LOKACIJA_ID,RESORT_ID,HOTEL_ID as they are from the same table, when I click in the navigator the button 'insert'. UPORABNIKI is translated USERS table.
edit : Inverted the query as suggested
I believe this is a case in which TDatasetProvider is unable to produce the right command to update the tables involved. In this cases, what I do is to add a handler to the TDatasetProvider.BeforeUpdateRecord event. This event will let you handle each operation worked on the dataset and produce the needed SQL statements to propertly persist those operations in the data server.
You will have to write the UPDATE/DELETE/INSERT statements yourself, but you will also have absolute power about how the tables are updated. That´s why I always use this event and never rely on TDatasetProvider intrinsec update process.
See the OnGetTableName event on the TDatasetProvider.
Also, I believe it would be better if you invert your query, i.e., use ... FROM UPORABNKI inner join LOKACIJE...
The OnGetTableName event from the TDataSetProvider has a var parameter called TableName. You should assign it to 'UPORABNIKI'.
Also, usually TDataSetProvider thinks that the table you use on the FROM clause is the one you want to update, so, if you change your query, the above event might not even be needed.
Hope that helps,
Marco

Adding data to a SQL table through a stored procedure?

I'm working on a learning project to understanding SQL. And one of the tasks I was given is:
For the "Create Order" button, create a stored procedure named [dbo].[CreateOrder] that creates a new order for the selected product and decrements the quantity of product in stock. If there is not enough product in stock to create the order the procedure should return a message stating so.
For this question I am given a DB that has several tables in it, but I'm not sure what the correct approach to solving this task is.
A pretty vague question, but I'd solve it with the following steps:
1) Creating a stored procedure that accepts parameters needed for the order.
2) Declare a varchar variable in the stored procedure to hold feedback for the user.
3) Using those parameters, check for product availability with a SELECT query in the stored procedure.
3) If the product is available, add the order to the appropriate table by adding an INSERT query to the stored procedure, and set the feedback variable to let the user know the order was inserted.
4) If the product is not available, set your feedback variable to say so.
5) Return the feedback variable to the user.
If you don't know how to perform any of those individual steps, hopefully that at least breaks the problem down into manageable bites to look up.