Oracle reflection of tuple on system level trigger to dynamically replace characters of interest
I am attempting to write a generic trigger that can inspect each tuple that is to be inserted into the database. In this inspection I want to replace characters that are of interest to me, such as the curly quotation mark (MS word) with the regular quotation mark ("). Since I do not want to have to write two replace calls for each column that stores data that the user supplies, nor do I want to do this in the application for a myriad of reasons. pseudocode as follows:
create or replace trigger changeChars
before insert
on @tableName
reference new as var
for开发者_StackOverflow each row
Begin
loop
:var.@column := replace(:var.@column,'”', '"');
end
Why do you want to replace these characters? A lot of folks have problems with characters like the Microsoft curly quotes because those characters are not supported by their database character set which, by default, causes character set conversion to replace those characters with replacement characters like '?'. If that is the problem that you're trying to solve, database code can't help because the character set conversion happens at the network layer before the data even gets to the database. Potentially, you could use Oracle Locale Builder to build a custom locale for your client machines that would allow you to specify different replacement characters (i.e. double-quotes rather than Microsoft curly quotes).
If your problem is not character set related, there is no way to create this sort of dynamic trigger. You could write a bit of dynamic SQL that created a trigger for every table that called REPLACE
on every VARCHAR2
column in the table. Of course, you'd need to maintain the trigger every time an additional VARCHAR2
column was added (either by changing the trigger or re-running the PL/SQL block).
Untested, and I'm sure the DDL generated isn't correct, but you'd want something like
DECLARE
l_sql_stmt VARCHAR2(4000);
BEGIN
FOR tbl IN (SELECT * FROM user_tables)
LOOP
l_sql_stmt := 'CREATE OR REPLACE TRIGGER ' || tbl.table_name || '_changeChars ' ||
' BEFORE INSERT ON ' || tbl.table_name ||
' FOR EACH ROW ' ||
'BEGIN ';
FOR col IN (SELECT * FROM user_tab_cols WHERE table_name = tbl.table_name)
LOOP
l_sql_stmt := l_sql_stmt ||
' :new.' || col.column_name ||
' := replace( :new.' || col.column_name || ',''"'', ''"''');';
END LOOP;
l_sql_stmt := l_sql_stmt ||
'END; ';
dbms_output.put_line( 'SQL statement = ' || l_sql_stmt );
EXECUTE IMMEDIATE l_sql_stmt;
END LOOP;
END;
Okay, so the easy bit is to build a function to do the replacement.
create or replace function strip_quotes
(p_str in varchar2)
return varchar2
is
begin
return translate(p_str, chr(145)||chr(146)||chr(147)||chr(148), chr(39)||chr(39)||chr(34)||chr(34));
end;
/
145 to 148 being the extended ASCII values for MS Smart Quotes, single and double. We can use it like this:
SQL> select strip_quotes('ôHiö said Jon.') from dual
2 /
STRIP_QUOTES('ôHIöSAIDJON.')
-------------------------------------------------------------------------------
"Hi" said Jon.
SQL>
The harder bit is the reflection. I agree with Justin, that generating a trigger for each table is the best solution.
精彩评论