Archive for November, 2006



Holy Disk Crash!

Saturday, November 25th, 2006

It’s funny, right after writing The Day the DB Died, the American Pie parody about a DBA that loses their database and their job due to not having backups…the disk dies on my non-backed-up dedicated server that houses this website. Oracle Alchemist was down for a while, but he wasn’t out.

Got a fresh drive put in, reinstalled the OS, mounted the failed drive (/tmp was corrupted) and copied everything back over…voila! Lead into gold, the Alchemist lives to blather on aimlessly another day.

Anyways, sorry for the inconvenience to anyone affected by this horrible and dire tragedy.

The Day the DB Died

Wednesday, November 22nd, 2006

I have a habit of writing parody songs about things I like, and Oracle is one of those things. I’ve never seen a full on Oracle parody song, so I will make the claim that this is the first. If it’s not, I’m sure it’s the first of this magnitude!

Now, the tragic epic of a DBA that allowed his system to go on without backups, to the tune of American Pie by Don McLean. It’s a bit long, so I have it with a “read more” link. Enjoy!


A long, long, time ago…
I can still remember
How an index used to make me smile.
And I knew if I had my chance
The database would be enhanced
And maybe it would run well for a while.

But doing upgrades made me shiver
With every version I delivered
Bad news in the GUI
The progress bar was screwy.

I can’t remember if I cried
When the data dictionary came out fried
I just wanted to run and hide
The day the DB died.

So bye bye to the database guys,
Sent a TAR to MetaLink,
But we got no reply
And the managers asked, “Is that all you can try,”
I said, this will be the day that it dies…
This will be the day that it dies.

(more…)

GQLPlus - Command Line Goodness

Friday, November 17th, 2006

I just found a link to a command line wrapper for SQL*Plus called GQLPlus. Supposedly this wrapper allows historical editing, table/column name completion, and more.

Even better, it’s just a precompiled drop-in with SQL Plus. Download and run; nothing else is required. From the website:

“gqlplus is a drop-in replacement for sqlplus, an Oracle SQL client, for UNIX and UNIX-like platforms. The difference between gqlplus and sqlplus is command-line editing and history, plus table-name and column-name completion. As you know if you have used sqlplus, it is notoriously difficult to correct typing errors and other mistakes in your SQL statements. sqlplus does give you ability to use external editor to edit a statement, but only the last statement you typed. gqlplus solves this problem by providing the familiar command-line editing and history as in tcsh or bash shells, and table/column-name completion, while otherwise retaining compatibility with sqlplus. Thus, no user training is needed - simply use gqlplus instead of sqlplus. In addition, configuration/installation is trivial: gqlplus is a single binary compiled executable (written in C), so all you need to do is download it and put it anywhere in your PATH. After that, you’ll be ready to use it.”

I haven’t used it yet, but it looks really cool. Anyone out there who has given it a shot?

Make History with Streams

Thursday, November 16th, 2006

Oracle Streams is an outstanding way to get data from one server to another. It is NOTHING like the advanced replication of old, which is why I call it “outstanding” and not “horribly inefficient and error-prone, not to mention annoying.”

If you don’t know anything about Streams, you can catch up by reading a good book, use a quick tip, or read some documentation.

Recently I was asked how to implement a custom INSERT handler for a streams setup. The client had the following situation:

  • A production table that housed important data
  • A history table that was inserted into every time a record on the production table changed
  • A remote database meant to hold history

The client wished to set up Streams as follows:

  • On insert to the history table, attempt to stream the row across to the target DB
  • On successful apply to the target DB, remove the row from the source DB history table
  • Do not propagate deletes from the source to the target

Thankfully, it was pretty easy. We used a combination of a negative propagation rule for deletes and an apply-side DML handler for inserts. Let’s take a look.

First Step: Halt Propagation of Deletes

This was accomplished using the ADD_TABLE_PROPAGATION_RULES procedure of the DBMS_STREAMS_ADM package with a negative rule on DELETE. A negative rule simply means that we want to set inclusion to FALSE if the condition is TRUE.


  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'streams_test.test_hist',
    streams_name            => 'prop_test',
    source_queue_name       => 'streamsadm.rep_capture_queue',
    destination_queue_name  => 'streamsadm.rep_dest_queue@targetdb',
    include_dml             => true,
    include_ddl             => true,
    source_database         => 'primarydb',
    inclusion_rule          => false,
    and_condition           => ':lcr.GET_COMMAND_TYPE() = ''DELETE''');
END;
/

As you can see, we have set up a rule for stream prop_test, on table streams_test.test_hist. This rule applies to rows coming from the rep_capture_queue locally and going to the rep_dest_queue on the target database. We say to include DML, and include DDL; however, we also say in the and_condition that if the LCR (Logical Change Request) type is DELETE, to NOT include it (inclusion_rule => false).

That takes care of deletes. No deletes on that table will propagate, though anything else will.

Create a Handler on the Target

The next step was to create a procedure on the target database that would be used as an Apply handler. This procedure is owned by streams_test, the actual user being streamed.


create or replace procedure streams_test.del_orig_lcr (in_any in sys.anydata) is
  lcr sys.lcr$_row_record;
  lcrrow sys.lcr$_row_list;
  rc pls_integer;
  pkdata sys.anydata;
  pkval number;
begin
  rc := in_any.getobject(lcr);
  lcrrow := lcr.GET_VALUES('NEW');
  lcr.execute(true);
  for i in 1..lcrrow.count
  loop
    if lcrrow(i).column_name = 'ID' THEN
      pkdata := lcrrow(i).data;
      rc := pkdata.getNumber(pkval);
      delete from streams_test.test_hist@primarydb where id = pkval;
      commit;
    end if;
  end loop;
end;
/

This little piece of code will accept an LCR (Logical Change Request), and extract the values. First, it executes the LCR on the apply side. If that is successful, it finds the primary key column (in this case ID), and deletes it from the remote database. Note that in order to do the delete, you will need a database link pointing back to the source DB. When the delete is processed on the source database, it wont propagate over to the target because of the rule we made above. If the insert to the target does not work, it will not delete from the source. We have covered all our bases to have a process that will ONLY delete the original if the apply works properly.

Last Step: The DML Handler

The last step is to tell Oracle that it should do something special when the apply process runs. To accomplish this, we will set up an apply handler on the target database (not the source):


BEGIN
  DBMS_APPLY_ADM.SET_DML_HANDLER(
     object_name		=>	'streams_test.test_hist',
     object_type		=>	'TABLE',
     operation_name		=>	'INSERT',
     error_handler		=>	false,
     user_procedure		=>	'streams_test.del_orig_lcr',
     apply_database_link	=>	null,
     apply_name			=>	null);
END;
/

As you can see, we tell the apply that if DML comes in of type INSERT, to run the streams_test.del_orig_lcr procedure. We’re rocking and rolling now!

The Test - Finale

The following test was run on the primary database:


SQL> select * from streams_test.test_hist;

no rows selected

SQL> insert into streams_test.test_hist values (2, 'Jeff', sysdate);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from streams_test.test_hist;

no rows selected

SQL> insert into streams_test.test_hist values (2, 'Kristina', sysdate);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from streams_test.test_hist;

no rows selected

SQL> select * from streams_test.test_hist@targetdb;

        ID CHARFIELD                                          THEDATE
---------- -------------------------------------------------- ---------
         1 Steve                                              10-NOV-06
         2 Kristina                                           10-NOV-06

SQL> insert into streams_test.test_hist values (2, 'Duplicate', sysdate);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from streams_test.test_hist;

        ID CHARFIELD                                          THEDATE
---------- -------------------------------------------------- ---------
         2 Duplicate                                          10-NOV-06

SQL> select * from streams_test.test_hist@targetdb;

        ID CHARFIELD                                          THEDATE
---------- -------------------------------------------------- ---------
         1 Steve                                              10-NOV-06
         2 Kristina                                           10-NOV-06

Conclusion

We inserted two records on the primary (source), and both of them propagated across to the target and deleted on the source side. However, when we tried to force an error with a duplicate entry for the PK on the ID column, notice that the row stayed in the table on the primary. This way, we are always guaranteed to keep our data, even if there is a streams error or some other kind of issue.

Internet Explorer Users Rejoice!

Monday, November 13th, 2006

It looks like I got everything looking good on IE6 and 7 again. If you notice any pages that don’t look right, please leave me a note by clicking on the Contact link.

Oracle.com

Friday, November 10th, 2006

Rise and Do Honor! Oracle Awards

Wednesday, November 1st, 2006

If you get the chance, check out the Oracle Award-Winners from the November/December edition of Oracle Magazine. You can find them at this link:

http://www.oracle.com/technology/oramag/oracle/06-nov/o66eca.html

I got the chance to meet a few of these fine people while at OpenWorld, and have spoken to others before and afterward as a fellow blogger. If you haven’t checked out Eddie Awad’s new OraDot.com OraNA, I’d highly recommend it. It’s like the Digg an outstanding news aggregator for Oracle, and has tons of great articles.

Why the strikethroughs? Read Eddie’s comment below. Thanks Eddie!

My APEX/AJAX Presentation at OpenWorld 2006

Wednesday, November 1st, 2006

Now that Oracle has officially made the OpenWorld 2006 presentations public, I figured I’d make mine available from my site. You can click the link below to download it in PDF format:

Putting the Express Back Into Oracle Application Express with AJAX

Enjoy! I am also working on getting the example I used in my presentation on a public site. Stay tuned!