We know that it is possible to dynamically figure out the name of the procedure or package that is currently executing as explained here and here. This generally applies to statements being executed from other stored procedures (compiled) in the database.
The problem:
We have been trying to log all UPDATE activity on a specific column (called STATE) by placing a trigger on the table and invoking who_called_me from within the trigger. The purpose of doing this is apparently as per the application design the column STATE could get updated by multiple pieces of code (residing in the database) based on certain business conditions. In addition to that, the column could also get updated by the application which is a hibernate based application and at times when the update happens by a hibernate query the who_called_me function returns nothing. There are multiple parts in the application that could also UPDATE the column STATE based on certain conditions.
The who_called_me strategy is working well for us in cases where a stored procedure (which resides in the database) issues the UPDATE statement and who_called_me is successfully capturing the corresponding owner, name, line no. etc. of the stored procedure. But in case the UPDATE happens from hibernate, the function captures no details.
Is there a way to capture which hibernate query UPDATEd the row through the trigger? Or is there any other way?
Note: The trigger code is similar to the answer posted on this question.
you can track the query with ora_sql_text function, e.g. this is the function I use for that:
-- getting sql code, which is calling the current event, as clob
function getEventSQLtext
return clob
is
sqllob clob;
sql_text ora_name_list_t;
dummy integer;
begin
dummy := ora_sql_txt(sql_text);
dbms_lob.createtemporary(sqllob,false);
for i in 1..sql_text.count loop
dbms_lob.writeappend(sqllob,length(sql_text(i)),sql_text(i));
end loop;
return sqllob;
if dummy is null then null; end if; -- removing warning of non-used variable :)
end;
This will be a query which is generated by hibernate and this is the only information you can get because this should be the only thing hibernate can do with DB.
It turns out, the who_called_me approach works better for stored procedure calls where the stack trace can point exactly which line invoked a DML. In, case of hibernate it is possible that the code may not call a stored procedure but in-turn may have individual DMLs which get invoked based on certain conditions. As opposed to other answer given by #simon, the ora_sql_txt function may only work in system event triggers or I may be wrong, but either way it is not capable of capturing the SQL Statement issued by Hibernate (tested that it does not works and retunrs a NULL value).
So at the end of the day, to find what SQL Hibernate is using, DB Trace files and Hibernate debug level logs is the only way for now.
Related
I did search for a similar question, but if I overlooked an existing answer I am glad to be redirected there.
I am working to untangle an Oracle Stored Proceedure in a legacy system written by a long departed developer.
The focus of the proceedure is to upload user data into the existing table structure in a bulk collection and save keystroke time adding 1-x-1 records.
The procedure appears to work without error and the user group would like to expand it to allow additional data to load to separate but related tables.
The author is using the NEXTVAL and CURRVAL commands to add primary key information as new records are added using the CSV data.
But I am confused because my understanding of NEXTVAL/CURRVAL was that they required context and declaration to be used correctly.
For example the Proceedure has the following:
SELECT seq_site.nextval INTO v_curr
FROM DUAL;
UPDATE temp_table
SET site_id = seq_site.currval
However [SEQ_SITE] is not declared anywhere in the preceding lines of the Procedure.
Am I inferring correctly that the clause [SELECT seq_site.nextval INTO v_curr] is the declaration for [SEQ_Record_count]?
(...v_curr is declared an integer early in the procedure declarations btw...)
I have ORM setup and working with Oracle on an existing database and have been able to get inserts to work when I access the sequence but because triggers were used in the original application the sequence skips a number.
Is there a way to get ORM to use the trigger?
Disabling the trigger is not an option since it is used by the existing app and cannot be disabled during migration.
component persistent="true" table="table_name" schema="schema_name" {
property name="table_id" column="table_id" fieldtype="id" generator="sequence" sequence="schema_name.sequence_name";
...
}
Triggers are not accessible program units. The only way to "call" a trigger is to execute the appropriate DML against the owning table.
There are two possible resolutions to your problem.
Rewrite the trigger. You say another application still needs the trigger to populate the ID, but you could change the trigger's logic with a conditional....
if :new.id is null then
:new.id := whatever_seq.nextval; --11g syntax for brevity
end if;
This will populate the ID when the other application insert into the table but won't overwrite your value.
Stop worrying. Sequences are merely generators of unique identifiers. The numbers ascend but it really doesn't matter if there are gaps. Unless you are handling billions of rows it is extremely unlikely your sequence will run out of numbers before your applications get retired.
Do you mean that the DB normally assigns an ID, using an insert trigger? That would explain why you're skipping a number. You could try generator="select" which will get hibernate to read the ID back after the insert has occurred (and the trigger has been fired). It's there to handle exactly the situation I think you're describing.
I need to write a sproc which performs some INSERTs on a table, and compile a list of "statuses" for each row based on how well the INSERT went. Each row will be inserted within a loop, the loop iterates over a cursor that supplies some values for the INSERT statement. What I need to return is a resultset which looks like this:
FIELDS_FROM_ROW_BEING_INSERTED.., STATUS VARCHAR2
The STATUS is determined by how the INSERT went. For instance, if the INSERT caused a DUP_VAL_ON_INDEX exception indicating there was a duplicate row, I'd set the STATUS to "Dupe". If all went well, I'd set it to "SUCCESS" and proceed to the next row.
By the end of it all, I'd have a resultset of N rows, where N is the number of insert statements performed and each row contains some identifying info for the row being inserted, along with the "STATUS" of the insertion
Since there is no table in my DB to store the values I'd like to pass back to the user, I'm wondering how I can return the info back? Temporary table? Seems in Oracle temporary tables are "global", not sure I would want a global table, are there any temporary tables that get dropped after a session is done?
If you are using Oracle 10gR2 or later then you should check out DML error logging. This basically does what you want to achieve, that is, it allows us to execute all the DML in a batch process by recording any errors and pressing on with the statements.
The principle is that we create an ERROR LOG table for each table we need to work with, using a PL/SQL built-in package DBMS_ERRLOG. Find out more. There is a simple extension to the DML syntax to log messages to the error log table. See an example here. This approach doesn't create any more objects than your proposal, and has the merit of using some standard Oracle functionality.
When working with bulk processing (that is, when using the FORALL syntax) we can trap exceptions using the built-in SQL%BULK_EXCEPTIONS collection. Check it out. It is possible to combine Bulk Exceptions with DML Error Logging but that may create problems in 11g. Find out more.
"Global" in the case of temporary tables just means they are permanent, it's the data which is temporary.
I would define a record type that matches your cursor, plus the status field. Then define a table of that type.
TYPE t_record IS
(
field_1,
...
field_n,
status VARCHAR2(30)
);
TYPE t_table IS TABLE OF t_record;
FUNCTION insert_records
(
p_rows_to_insert IN SYS_REFCURSOR
)
RETURN t_table;
Even better would be to also define the inputs as a table type instead of a cursor.
Recently, I encountered a BEFORE INSERT OR UPDATE trigger on a table. In this trigger, the author relies on the INSERTING and UPDATING functions (both return a BOOLEAN) of the DBMS_STANDARD package to determine if the trigger was fired before an insert or before an update.
For example:
CREATE OR REPLACE TRIGGER CUSTOMER_TRIGGER
BEFORE INSERT OR UPDATE ON CUSTOMER
FOR EACH ROW
BEGIN
IF INSERTING THEN
/* Some code */
END IF;
IF UPDATING THEN
/* Some other code */
END IF;
END;
Yes, I know that two, individual triggers could have been written to handle the two events separately. That's not the point of this question.
After troubleshooting an error being received by these functions, we received word (from Oracle Support) that "dbms_standard routines are not really meant to be called by user programs". Is this true?
I find this a little strange, considering other procedures (such as RAISE_APPLICATION_ERROR and COMMIT) are commonly used in PL/SQL code.
The functions INSERTING, UPDATING and DELETING are expressly provided for use in writing trigger code (see trigger documentation), so there is absolutely no proscription against using those. Similarly, RAISE_APPLICATION_ERROR is documented to be intended for use by developers.
Having just DESCribed DBMS_STANDARD there are certainly some functions in there I don't know about and that perhaps shouldn't be used in your own code (for all I know) such as GRANTEE.
Generally, if you can find Oracle official documentation telling you how to use something, then it is OK to use it.
I have two procedures A and B. Procedure A performs certain tasks. Procedure B has to monitor how many times procedure A is called in a day.
How to achieve this?
Add a statement to the procedure:
update statistics_table
set proc_a_count = proc_a_count + 1;
Of course, you'll have to create a suitable table to hold the count and initialize it with a zero in the field.
insert a row into a log table.
Oracle does not track this sort of thing by default but if you just want to record some simple information then switch on the built-in AUDIT functionality:
AUDIT EXECUTE PROCEDURE BY ACCESS;
You can view the accesses in the view dba_audit_trail. Find out more.
If for some reason you don't want to use the audit trail - say you want to capture more information - then you will need to use your own logging mechanism. This is a good use for the AUTONOMOUS TRANSACTION pragma. Just be careful that writing the log records doesn't have an undue impact on the performance of your application.
edit
The role of procedure B in your question is entirely superfluous: either the database records how often procedure A runs or else A writes its own trace records. Unless B is a packaged query on the log (however implemented)?