Severin asked if I thought OO had fulfilled its promise.
Well, that's a question that almost by nature is impossible to answer.
Lots of colleges are teaching object-oriented programming right from the start, and lots of programmers adopted that style once they saw it becoming popular, so "widely adopted", sure.
For "fulfilled its promise", what exactly is the promise? If we think of people 100 years ago chopping down trees with axes and handsaws, and then the chainsaw came along, obviously a lot more people cut down a lot more trees today. But the professionals have in many cases cut down all the trees, which turned out not to be such a great idea. And the amateurs prune the trees in their back yards, but sometimes they cut off their own fingers as well.
When I run through this analogy in my mind, I can't help thinking that in the world of computing, our "chainsaw" -- whatever is the latest programming or implementation fad -- is often marketed both as a children's toy and as a personal grooming aid. :-)
OO style is a natural outgrowth when you're reaching the limits of other styles. For example, if you're doing functional programming and finding yourself writing slight variations of the same code over and over. Or, in the Oracle context, if you're writing procedural PL/SQL and find yourself wishing that you could plug in a variable at a spot where one isn't allowed, or you're writing the same code twice to deal with variables of different types.
But when I see people tackling straightforward problems requiring small amounts of code, sometimes I feel like it's overkill. I've seen plenty of code where someone contrived a hierarchy where one wasn't really needed, or where a lot of team effort went into making the hierarchy deeper, rather than coding the lowest-level classes that would actually do something useful.
Think of the common OO idiom of hiding all member variables behind getXYZ() and setXYZ() methods. If your project is going to employ tools that generate and compile source code dynamically, or a debugger that's going to hook in its own get and set methods ahead of the real ones, that technique makes perfect sense. It's enabling extra functionality, it's planning ahead to avoid problems in scalability and maintenance. But many programs are written to solve some limited problem, and the code is never going to be reused on such a scale, in which case the extra typing might not serve any purpose.
I like the way UC Berkeley does it in their introductory CS course (link goes to UCB course podcasts on iTunes). They run through various styles of programming, and only when they've demonstrated some of the limitations that OO is intended to solve do they introduce OO style. At that point, the assignments involve actually writing the guts of an OO system, so even if you're just running trivial OO code you're seeing how it all works under the covers.
Saturday, March 22, 2008
Tuesday, March 11, 2008
Can Your Programming Language Do This... Or That...
Just wanted to point out this "Joel On Software" article, Can Your Programming Language Do This?. It's a nice concise opinion that summarizes why, given an arbitrary algorithmic problem, I'm personally more likely to turn to Javascript (or Perl or another scripting language with the same feel) than Java.
The key for me with Java is this statement from Joel:
I think of Java's OO nature as "hard" object orientation, in that choices are relatively set in stone based on how class files are organized and how the code flows. You can make variants of a class that do whatever you want, but you might have to do a lot of advance planning.
Conversely, in Perl, you can do like so:
sub something
{
...one set of code...
}
sub something
{
...another set of code...
}
You can call something(), and it picks up the last definition. Which makes it very easy to change the behavior of a program by overriding a method right before you call it, or by swapping the order of 'require' lines, or what have you. I think of this as "soft" object orientation, because it may be less amenable to mathematical proof of correctness, but it requires less planning and is less likely to produce cascades of syntax errors. Define it, call it, redefine it, call the new thing.
Most languages have some sort of dynamic execution facility. For example, with PL/SQL it's the EXECUTE IMMEDIATE statement and the OPEN-FOR statement. Any time you see a language concatenating strings and executing the result, you need to watch out for potential security problems. Who gets to enter that string, could it be rigged to execute multiple statements instead of one, could different names be substituted so it operated on some object you didn't expect. PL/SQL has the USING clause to guard against such problems by using bind values. Still, the Javascript approach of passing around functions as parameters feels comfortable, because all the variations of those functions are defined in your own source code rather than combined from arbitrary strings.
The key for me with Java is this statement from Joel:
Java required you to create a whole object with a single method called a functor if you wanted to treat a function like a first class object. Combine that with the fact that many OO languages want you to create a whole file for each class, and it gets really klunky fast.
I think of Java's OO nature as "hard" object orientation, in that choices are relatively set in stone based on how class files are organized and how the code flows. You can make variants of a class that do whatever you want, but you might have to do a lot of advance planning.
Conversely, in Perl, you can do like so:
sub something
{
...one set of code...
}
sub something
{
...another set of code...
}
You can call something(), and it picks up the last definition. Which makes it very easy to change the behavior of a program by overriding a method right before you call it, or by swapping the order of 'require' lines, or what have you. I think of this as "soft" object orientation, because it may be less amenable to mathematical proof of correctness, but it requires less planning and is less likely to produce cascades of syntax errors. Define it, call it, redefine it, call the new thing.
Most languages have some sort of dynamic execution facility. For example, with PL/SQL it's the EXECUTE IMMEDIATE statement and the OPEN-FOR statement. Any time you see a language concatenating strings and executing the result, you need to watch out for potential security problems. Who gets to enter that string, could it be rigged to execute multiple statements instead of one, could different names be substituted so it operated on some object you didn't expect. PL/SQL has the USING clause to guard against such problems by using bind values. Still, the Javascript approach of passing around functions as parameters feels comfortable, because all the variations of those functions are defined in your own source code rather than combined from arbitrary strings.
Friday, January 18, 2008
Relational vs. OO Databases
I found this ACM Queue article interesting:
A Conversation with Michael Stonebraker and Margo Seltzer
The most interesting part for me was the rumination on "Why did OO (object-oriented) databases fail?". I worked on the "Oracle Database Application Developer's Guide - Object-Oriented Features" back in 8i, and heard the mantras about how OO would kill off relational. Previously, back at IBM, I was involved with C++ coding, learning C++ from GOF member John Vlissides, RIP. Then I saw as IBM moved into Java, a lot of code that I felt misconstrued the idea of object orientation, and a lot of instructional material that I felt was kinda lacking. (A car and a truck are both kinds of vehicles; now build a class hierarchy for handling events in a GUI. :-)
So, I was interested to see Stonebraker's take on the potential market opportunity for OO database, and pragmatic reasons why it didn't take over the world.
I have in mind several more blog posts on various aspects of object orientation and how it plays out in database and non-database contexts.
A Conversation with Michael Stonebraker and Margo Seltzer
The most interesting part for me was the rumination on "Why did OO (object-oriented) databases fail?". I worked on the "Oracle Database Application Developer's Guide - Object-Oriented Features" back in 8i, and heard the mantras about how OO would kill off relational. Previously, back at IBM, I was involved with C++ coding, learning C++ from GOF member John Vlissides, RIP. Then I saw as IBM moved into Java, a lot of code that I felt misconstrued the idea of object orientation, and a lot of instructional material that I felt was kinda lacking. (A car and a truck are both kinds of vehicles; now build a class hierarchy for handling events in a GUI. :-)
So, I was interested to see Stonebraker's take on the potential market opportunity for OO database, and pragmatic reasons why it didn't take over the world.
I have in mind several more blog posts on various aspects of object orientation and how it plays out in database and non-database contexts.
Friday, January 11, 2008
8 Things About Me
I don't do a lot of blogrolling and haven't been tagged yet in Jake's Oracle blogger game of tag. So I'll seize the initiative and post my 8 +/ 1 things anyway.
1. I'm Canadian, and in my time at Oracle have cycled through a special visa for Canadians, then the infamous H1-B, and finally a Green Card.
2. I come from the most recent, and relatively little known, part of Canada: the island of Newfoundland. This means I should by rights have a strong Irish-tinged accent. But for some reason I don't. Pity, as having an Australian, New Zealand, English, or even Welsh accent seems to be a plus at Oracle. :-) Still, I do spell honour, colour, and flavour the correct way. One of the things you can do on a slow day in Newfoundland, is drive down to the local lighthouse and take turns being the easternmost person in North America.
3. I am a pretty good tennis player. When Canada compiled its computerized tennis rankings in the mid-80s, I was in the top 100 national players, but only because they didn't have a very good ranking algorithm at first. I competed for Newfoundland in tennis in the 1985 Canada Games, and was also certified in Canada as a tennis coach. Most strategies that I employ in programming, business, and life trace back in some way to tennis.
4. On my first night in Toronto, I got lost driving at night, by coincidence right by the IBM lab where I would soon work. On my first trip to Berkeley, I got lost driving at night, by coincidence up in the hills a few blocks from where I now live.
5. I was in charge of all the Ada manuals at IBM for pretty much the whole time that IBM put out an Ada compiler. (Which makes designing and coding in PL/SQL feel like coming home.) My crunchiest deadline involved working 72 hours straight when the dates for 2 products fell into perfect alignment. My fondest accomplishment was learning REXX in one day to turn the Ada Language Reference from a plain vanilla text document into a complete hypertext system, after others estimated it as a 40-week project. My dullest piece of drudge work was a tie -- creating 24 separate disk labels and associated tracking paperwork for each one, for an early workstation compiler; and photostatting our original paper copy of the Ada Language Reference that we dug out of the archives for each release. I was assigned to Ada because it was listed on my resume, but in the "Programming Languages" course where I learned it, the DEC compiler didn't arrive in time, so I never actually used Ada for an assignment.
And today, Ada still is only a single word on my resume under "languages known"!
5b. I could go on for much longer about assembler than about Ada. :-)
5c. I also can write a sentence that uses both "FORTRAN" and "Fortran", correctly. Redoing all the IBM XL Fortran manuals was my favourite piece of writing.
6. I play the trombone, and have the stereotypical trombone player physique, temperament, and progression through different styles of facial hair. I didn't know any of that when they asked us in Grade 5 what instrument we wanted to play. I started playing in the Newfoundland Symphony Orchestra while still in my high school band, skipping the usual step of the Youth Orchestra.
7. If you ask why I moved from Canada to the US, I can spin you a tale about a life-changing epiphany over Ethiopian food, a drunken New Year's Eve revelation, a rant about living in Toronto, or a lament about the metric system or Lotus Notes. Depends on which reason you would find most entertaining. Now that the Canadian dollar is on par with the US dollar, I might need to revise some of those tales.
8. I appeared on TV in Canada in "Reach for the Top", a high school quiz show where our school reached the national finals. I have not parlayed that experience into an appearance on Jeopardy!, although I have qualified twice for their contestant pool. Our Reach for the Top team practiced with homebrew buzzers designed by the future founder of Research in Motion.
1. I'm Canadian, and in my time at Oracle have cycled through a special visa for Canadians, then the infamous H1-B, and finally a Green Card.
2. I come from the most recent, and relatively little known, part of Canada: the island of Newfoundland. This means I should by rights have a strong Irish-tinged accent. But for some reason I don't. Pity, as having an Australian, New Zealand, English, or even Welsh accent seems to be a plus at Oracle. :-) Still, I do spell honour, colour, and flavour the correct way. One of the things you can do on a slow day in Newfoundland, is drive down to the local lighthouse and take turns being the easternmost person in North America.
3. I am a pretty good tennis player. When Canada compiled its computerized tennis rankings in the mid-80s, I was in the top 100 national players, but only because they didn't have a very good ranking algorithm at first. I competed for Newfoundland in tennis in the 1985 Canada Games, and was also certified in Canada as a tennis coach. Most strategies that I employ in programming, business, and life trace back in some way to tennis.
4. On my first night in Toronto, I got lost driving at night, by coincidence right by the IBM lab where I would soon work. On my first trip to Berkeley, I got lost driving at night, by coincidence up in the hills a few blocks from where I now live.
5. I was in charge of all the Ada manuals at IBM for pretty much the whole time that IBM put out an Ada compiler. (Which makes designing and coding in PL/SQL feel like coming home.) My crunchiest deadline involved working 72 hours straight when the dates for 2 products fell into perfect alignment. My fondest accomplishment was learning REXX in one day to turn the Ada Language Reference from a plain vanilla text document into a complete hypertext system, after others estimated it as a 40-week project. My dullest piece of drudge work was a tie -- creating 24 separate disk labels and associated tracking paperwork for each one, for an early workstation compiler; and photostatting our original paper copy of the Ada Language Reference that we dug out of the archives for each release. I was assigned to Ada because it was listed on my resume, but in the "Programming Languages" course where I learned it, the DEC compiler didn't arrive in time, so I never actually used Ada for an assignment.
And today, Ada still is only a single word on my resume under "languages known"!
5b. I could go on for much longer about assembler than about Ada. :-)
5c. I also can write a sentence that uses both "FORTRAN" and "Fortran", correctly. Redoing all the IBM XL Fortran manuals was my favourite piece of writing.
6. I play the trombone, and have the stereotypical trombone player physique, temperament, and progression through different styles of facial hair. I didn't know any of that when they asked us in Grade 5 what instrument we wanted to play. I started playing in the Newfoundland Symphony Orchestra while still in my high school band, skipping the usual step of the Youth Orchestra.
7. If you ask why I moved from Canada to the US, I can spin you a tale about a life-changing epiphany over Ethiopian food, a drunken New Year's Eve revelation, a rant about living in Toronto, or a lament about the metric system or Lotus Notes. Depends on which reason you would find most entertaining. Now that the Canadian dollar is on par with the US dollar, I might need to revise some of those tales.
8. I appeared on TV in Canada in "Reach for the Top", a high school quiz show where our school reached the national finals. I have not parlayed that experience into an appearance on Jeopardy!, although I have qualified twice for their contestant pool. Our Reach for the Top team practiced with homebrew buzzers designed by the future founder of Research in Motion.
Monday, January 7, 2008
Search results as RSS
Normally an Oracle doc search gives you a regular HTML (really XHTML) results page:
http://www.oracle.com/pls/db111/search?word=web+services
Recently a developer asked for an RSS-format equivalent, to consume the results as a web service. At your, er, service:
http://www.oracle.com/pls/db111/search?word=web+services&format=rss
The format/structure of the RSS feed may change. For example, Firefox doesn't display the descriptions when it shows the feed page, because of the way the tags are nested.
This has me thinking of other places to use RSS feeds for doc-related items. The other place that immediately jumps to mind is in the list of books, to have an RSS feed of the most-recently updated books. That involves a few design decisions -- what exactly is the "pubDate" given that the generation of the HTML, the posting of the library on OTN, and the build of the search index that generates the RSS feed might all be on slightly different days?
I'm interested in any other ideas about RSS feeds for the type of metadata that's available from a documentation library.
http://www.oracle.com/pls/db111/search?word=web+services
Recently a developer asked for an RSS-format equivalent, to consume the results as a web service. At your, er, service:
http://www.oracle.com/pls/db111/search?word=web+services&format=rss
The format/structure of the RSS feed may change. For example, Firefox doesn't display the descriptions when it shows the feed page, because of the way the tags are nested.
This has me thinking of other places to use RSS feeds for doc-related items. The other place that immediately jumps to mind is in the list of books, to have an RSS feed of the most-recently updated books. That involves a few design decisions -- what exactly is the "pubDate" given that the generation of the HTML, the posting of the library on OTN, and the build of the search index that generates the RSS feed might all be on slightly different days?
I'm interested in any other ideas about RSS feeds for the type of metadata that's available from a documentation library.
Sunday, December 30, 2007
What Is Scripting, Anyway?
[Some thoughts prompted by the article "Programming is Hard, Let's Go Scripting" by Larry Wall, father of Perl.]
How to define scripting and scripting languages? For me, as a long-time delver into compiler details, there's one key distinction between scripting languages and other ones. Scripting languages bypass all the low-level conventions for interlanguage calls, and take the shortcut of encoding everything as text. Usually, such conventions take up one or more chapters in a compiler manual -- how is a float represented, are integers big-endian, what's the memory layout of an array, structure, etc. The scripting language expects that all of this will be passed around in ASCII format via pipes, text files, and command-line flags.
Unix shell scripting is the natural building block for all of this. Consider a pipeline like this:
du -k | sort -n | grep -i Downloads
The first command gives the size of each subdirectory starting with the current directory. The second command sorts this output while treating the first field as a number; for example, "99 Directory 1" comes before "100 Directory 2", even though a strict alpha sort would put "1" before "9". And the third command plucks out lines containing a certain string; the directory names didn't get lost while we were doing that numeric sorting.
Perl descends from this lineage with its backticks, commands like eval and grep, and HERE documents. All very familiar. But Perl's object orientation really strays from its roots as a scripting language. All of a sudden, you have to think intently about how hashes are laid out. You can write script-like programs using Perl's object-oriented notation with packages written by someone else, but the OO code behind your own packages doesn't feel very scriptish. (In the same way that C isn't a scripting language, even though grep et al are written in C.)
The nicest Perl touch I see in this area is in conversion between strings and numbers. For example, you can say '$foo = "00000";', then do various operations like $foo++, and you'll find the value gets printed as "00001", "00002", etc. Very nice for formatting sequence numbers for IDs.
Early vs. late binding. Now we're getting into familiar territory for Oracle developers. By default, most PL/SQL uses early binding -- it's easy to know ahead of time which tables, columns, types, etc. a PL/SQL subprogram will use, and whether it can use them successfully. (Just look at whether the stored procedure or function is valid.) Venture into dynamic SQL, such as the EXECUTE IMMEDIATE command, and now it's not so easy to guarantee correctness or reliability. One mistake in string concatenation logic, and you'll try to reference a non-existent table or column, send badly-formed literals, and so on -- whole new classes of errors you never had to deal with before. Normally, I come down on the side that favors greater flexibility, but I haven't found PL/SQL's restrictions to be too onerous considering how important it is to ensure data integrity. Other aspects of PL/SQL that incorporate late binding -- hence are more comfortable for long-time scripters -- are the anonymous block (and it's always fun to generate the code for an anonymous block from some other program written in Perl or what have you), and the %TYPE and %ROWTYPE qualifiers, which insulate programs to some degree against changes wrought by ALTER TABLE.
Wordiness vs. use of symbols. Here is where I could imagine some improvements in PL/SQL. Why not allow { } instead of BEGIN/END, LOOP/END LOOP, and THEN/END IF for blocks? As Larry notes, scripting languages occupy the whole spectrum when it comes to verbosity.
PL/SQL also has the historical capitalization convention of all-caps for built-ins, and lowercase for user-defined names. Here, I firmly agree with Larry:
It's really the subprogram calls and variable references you want to see clearly when skimming code. As someone involved with doc, I know how nice it is to write "the ALTER TABLE statement" and know the emphasis will be preserved in all printed incarnations, even down to man pages, in a way that isn't guaranteed for bold, italics, or monospace. Yet a concern for code clarity means I'm perfectly happy writing INSERT, BEGIN, etc. in uppercase in running text, and lowercase in my own code. PL/SQL also has the conundrum of so many Oracle-supplied packages. Since DBMS_OUTPUT.PUT_LINE comes from Oracle, should it be written all-caps? Or since it obeys the same lexical rules as some package and subprogram you write, should it be lowercase? I prefer to stick with lowercase for everything in source code, even if that means terms can't be cut and pasted between code and running text.
How to define scripting and scripting languages? For me, as a long-time delver into compiler details, there's one key distinction between scripting languages and other ones. Scripting languages bypass all the low-level conventions for interlanguage calls, and take the shortcut of encoding everything as text. Usually, such conventions take up one or more chapters in a compiler manual -- how is a float represented, are integers big-endian, what's the memory layout of an array, structure, etc. The scripting language expects that all of this will be passed around in ASCII format via pipes, text files, and command-line flags.
Unix shell scripting is the natural building block for all of this. Consider a pipeline like this:
du -k | sort -n | grep -i Downloads
The first command gives the size of each subdirectory starting with the current directory. The second command sorts this output while treating the first field as a number; for example, "99 Directory 1" comes before "100 Directory 2", even though a strict alpha sort would put "1" before "9". And the third command plucks out lines containing a certain string; the directory names didn't get lost while we were doing that numeric sorting.
Perl descends from this lineage with its backticks, commands like eval and grep, and HERE documents. All very familiar. But Perl's object orientation really strays from its roots as a scripting language. All of a sudden, you have to think intently about how hashes are laid out. You can write script-like programs using Perl's object-oriented notation with packages written by someone else, but the OO code behind your own packages doesn't feel very scriptish. (In the same way that C isn't a scripting language, even though grep et al are written in C.)
The nicest Perl touch I see in this area is in conversion between strings and numbers. For example, you can say '$foo = "00000";', then do various operations like $foo++, and you'll find the value gets printed as "00001", "00002", etc. Very nice for formatting sequence numbers for IDs.
Early vs. late binding. Now we're getting into familiar territory for Oracle developers. By default, most PL/SQL uses early binding -- it's easy to know ahead of time which tables, columns, types, etc. a PL/SQL subprogram will use, and whether it can use them successfully. (Just look at whether the stored procedure or function is valid.) Venture into dynamic SQL, such as the EXECUTE IMMEDIATE command, and now it's not so easy to guarantee correctness or reliability. One mistake in string concatenation logic, and you'll try to reference a non-existent table or column, send badly-formed literals, and so on -- whole new classes of errors you never had to deal with before. Normally, I come down on the side that favors greater flexibility, but I haven't found PL/SQL's restrictions to be too onerous considering how important it is to ensure data integrity. Other aspects of PL/SQL that incorporate late binding -- hence are more comfortable for long-time scripters -- are the anonymous block (and it's always fun to generate the code for an anonymous block from some other program written in Perl or what have you), and the %TYPE and %ROWTYPE qualifiers, which insulate programs to some degree against changes wrought by ALTER TABLE.
Wordiness vs. use of symbols. Here is where I could imagine some improvements in PL/SQL. Why not allow { } instead of BEGIN/END, LOOP/END LOOP, and THEN/END IF for blocks? As Larry notes, scripting languages occupy the whole spectrum when it comes to verbosity.
PL/SQL also has the historical capitalization convention of all-caps for built-ins, and lowercase for user-defined names. Here, I firmly agree with Larry:
Actually, there are languages that do it even worse than COBOL. I remember one Pascal variant that required your keywords to be capitalized so that they would stand out. No, no, no, no, no! You don't want your functors to stand out. It's shouting the wrong words: IF! foo THEN! bar ELSE! baz END! END! END! END!
It's really the subprogram calls and variable references you want to see clearly when skimming code. As someone involved with doc, I know how nice it is to write "the ALTER TABLE statement" and know the emphasis will be preserved in all printed incarnations, even down to man pages, in a way that isn't guaranteed for bold, italics, or monospace. Yet a concern for code clarity means I'm perfectly happy writing INSERT, BEGIN, etc. in uppercase in running text, and lowercase in my own code. PL/SQL also has the conundrum of so many Oracle-supplied packages. Since DBMS_OUTPUT.PUT_LINE comes from Oracle, should it be written all-caps? Or since it obeys the same lexical rules as some package and subprogram you write, should it be lowercase? I prefer to stick with lowercase for everything in source code, even if that means terms can't be cut and pasted between code and running text.
Friday, December 28, 2007
Demystifying AJAX for the PL/SQL Developer
Raise your hand if you haven't heard that AJAX is the web development buzzword du jour. Despite the word "Javascript" in the acronym -- Asynchronous Javascript and XML -- PL/SQL can play a role in the front and particularly the back end. PL/SQL developers have the kind of background that's needed to understand what's happening at the lowest levels of network requests, perceived performance, and dealing with result sets. Don't be intimidated by the conventional wisdom that you should leave all the details to someone else's Javascript library. The code you need to write isn't all that big or complicated compared with 2-phase commits and bulk SQL.
Why AJAX at all? Think of it purely as an aspect of performance. You know how you run the UNIX 'time' command and see that a process took 10 minutes of user time, but only a few seconds of system time? Or how a PL/SQL anonymous block that runs for a long time produces no output from DBMS_OUTPUT.PUT_LINE until the very last moment, then dumps all the text at super speed? What you're dealing with is actual versus perceived performance, and web pages suffer from the same conundrum.
For example, when you load a page with a lot of graphics, the way the page is structured determines whether you'll see the text appear while the graphics are still loading, or if the page will be blank until every graphic is ready to display. Ditto with Javascript. If you have a big block of Javascript code on the page, the page might not render -- or might render but not respond to keyboard or mouse input -- until the Javascript is finished loading or (in worst case) until it's finished running. You can do some things to partially work around this, like hooking into the onload event handler, but still the page can seem slow, even though you know the back-end processing that generates it takes no time at all. The slowdown is in transmitting the code and data for the Javascript part of the page, and the browser parsing it and acting on it.
AJAX deals with that problem by making just-in-time requests for additional data, not when the page is loaded but when some action is taken, like clicking a button or link. The Javascript code receives the data as a block of text. Ideally, that text is supposed to be XML (hence the X in AJAX). But often, the most pragmatic approach is to send plain text, or HTML tagged text that is only a partial document. These approaches have even spawned their own acronyms; I like one known as AHAH, where the request returns a block of HTML that is inserted directly into the page at a designated spot.
If you're still waiting for me to mention something related to PL/SQL, I have good news and bad news. Bad news first: to do any of the "modern" web techniques, you will need to use a combination of at least 2 languages, in this case PL/SQL, Javascript, and optionally something else like PHP. The good news is, all of the components of an AJAX solution can be generated as PL/SQL stored procedures exposed through mod_plsql. That is, one stored procedure can generate the original HTML page that does the fancy AJAX stuff; another stored procedure can generate the Javascript loaded by the <script src="..."> tag; and yet another can do a database query or grab information through UTL_HTTP, UTL_FILE, etc. and return the text that gets passed to Javascript.
That last part is the most powerful, but also the biggest mental hurdle to get past. You are used to writing a procedure that generates an entire HTML page. Now you are writing a procedure that acts like a function, using HTP.P() to build a result set that the other end will parse as a single text string. (That's why a developer used to enterprise-class programming would probably choose to use a real XML document as the medium of exchange, to guard against the carelessness that comes with free-format text.)
On the Javascript end, you need a function to create the object that does the network request. You need a function that responds to some event on the page and initiates the request. And you also need a callback function that wakes up when the data is ready, receives the output, and then does the needful to change the page content and/or form elements. PL/SQL is really only required to do the database query and return the text of the results; the actual page and the Javascript code could come from static files, PHP, or some other option.
Within the HTML page that the user interacts with, you might also find that you need certain structures to be in place, or certain elements to have IDs. For example, one popular technique uses an empty <div> tag like so:
<div id="content_goes_here"></div>
The Javascript can locate the div element by looking up its ID, then set its innerHTML property to make an elaborate HTML structure like a table appear at that location, instead of a blank space.
You'll quickly find when you start implementing AJAX solutions, that you need a ton of these little Javascript functions, 2 for every kind of AJAXy change you're making on the page. The need to either auto-generate such functions, or to parameterize them to handle all kinds of cases, is why people recommend using the popular Javascript libraries. But there's nothing magic happening.
This post is going to be a bit light on source code, because the PL/SQL aspect of the front end is both so simple:
htp.p(q'{
/* enormous block of Javascript */
}');
and so complex and varied depending on your circumstances:
htp.p(q'{
try ... /* throws exception only in browser XYZ */
catch ...
try ... /* throws exception if HTML not structured as expected */
catch ...
}');
The techniques I use in my own code are probably lower level than you would use in yours, because I'm not using Javascript libraries like Prototype. So I'll refer you to mainstream AJAX tutorials to learn that part.
It's more straightforward on the back end. Here is PL/SQL code for a minimal AJAX procedure:
create or replace procedure ajax_procedure
(
param1 integer := null
, param2 varchar2 := null
)
as
begin
owa_util.mime_header('text/html');
htp.p('Some text from the procedure...');
end;
/
The XMLHTTPRequest object's open() method specifies the mod_plsql URL to the PL/SQL procedure, including any parameters as name/value pairs in the query string. Once the request has been fully satisfied (readyState == 4, status == 200), the object's responseText() method will return all the text that the procedure printed with htp.p(), which typically would be built up from some database logic. If the output was a real XML document, you would retrieve it through the object's responseXML method.
Why AJAX at all? Think of it purely as an aspect of performance. You know how you run the UNIX 'time' command and see that a process took 10 minutes of user time, but only a few seconds of system time? Or how a PL/SQL anonymous block that runs for a long time produces no output from DBMS_OUTPUT.PUT_LINE until the very last moment, then dumps all the text at super speed? What you're dealing with is actual versus perceived performance, and web pages suffer from the same conundrum.
For example, when you load a page with a lot of graphics, the way the page is structured determines whether you'll see the text appear while the graphics are still loading, or if the page will be blank until every graphic is ready to display. Ditto with Javascript. If you have a big block of Javascript code on the page, the page might not render -- or might render but not respond to keyboard or mouse input -- until the Javascript is finished loading or (in worst case) until it's finished running. You can do some things to partially work around this, like hooking into the onload event handler, but still the page can seem slow, even though you know the back-end processing that generates it takes no time at all. The slowdown is in transmitting the code and data for the Javascript part of the page, and the browser parsing it and acting on it.
AJAX deals with that problem by making just-in-time requests for additional data, not when the page is loaded but when some action is taken, like clicking a button or link. The Javascript code receives the data as a block of text. Ideally, that text is supposed to be XML (hence the X in AJAX). But often, the most pragmatic approach is to send plain text, or HTML tagged text that is only a partial document. These approaches have even spawned their own acronyms; I like one known as AHAH, where the request returns a block of HTML that is inserted directly into the page at a designated spot.
If you're still waiting for me to mention something related to PL/SQL, I have good news and bad news. Bad news first: to do any of the "modern" web techniques, you will need to use a combination of at least 2 languages, in this case PL/SQL, Javascript, and optionally something else like PHP. The good news is, all of the components of an AJAX solution can be generated as PL/SQL stored procedures exposed through mod_plsql. That is, one stored procedure can generate the original HTML page that does the fancy AJAX stuff; another stored procedure can generate the Javascript loaded by the <script src="..."> tag; and yet another can do a database query or grab information through UTL_HTTP, UTL_FILE, etc. and return the text that gets passed to Javascript.
That last part is the most powerful, but also the biggest mental hurdle to get past. You are used to writing a procedure that generates an entire HTML page. Now you are writing a procedure that acts like a function, using HTP.P() to build a result set that the other end will parse as a single text string. (That's why a developer used to enterprise-class programming would probably choose to use a real XML document as the medium of exchange, to guard against the carelessness that comes with free-format text.)
On the Javascript end, you need a function to create the object that does the network request. You need a function that responds to some event on the page and initiates the request. And you also need a callback function that wakes up when the data is ready, receives the output, and then does the needful to change the page content and/or form elements. PL/SQL is really only required to do the database query and return the text of the results; the actual page and the Javascript code could come from static files, PHP, or some other option.
Within the HTML page that the user interacts with, you might also find that you need certain structures to be in place, or certain elements to have IDs. For example, one popular technique uses an empty <div> tag like so:
<div id="content_goes_here"></div>
The Javascript can locate the div element by looking up its ID, then set its innerHTML property to make an elaborate HTML structure like a table appear at that location, instead of a blank space.
You'll quickly find when you start implementing AJAX solutions, that you need a ton of these little Javascript functions, 2 for every kind of AJAXy change you're making on the page. The need to either auto-generate such functions, or to parameterize them to handle all kinds of cases, is why people recommend using the popular Javascript libraries. But there's nothing magic happening.
This post is going to be a bit light on source code, because the PL/SQL aspect of the front end is both so simple:
htp.p(q'{
/* enormous block of Javascript */
}');
and so complex and varied depending on your circumstances:
htp.p(q'{
try ... /* throws exception only in browser XYZ */
catch ...
try ... /* throws exception if HTML not structured as expected */
catch ...
}');
The techniques I use in my own code are probably lower level than you would use in yours, because I'm not using Javascript libraries like Prototype. So I'll refer you to mainstream AJAX tutorials to learn that part.
It's more straightforward on the back end. Here is PL/SQL code for a minimal AJAX procedure:
create or replace procedure ajax_procedure
(
param1 integer := null
, param2 varchar2 := null
)
as
begin
owa_util.mime_header('text/html');
htp.p('Some text from the procedure...');
end;
/
The XMLHTTPRequest object's open() method specifies the mod_plsql URL to the PL/SQL procedure, including any parameters as name/value pairs in the query string. Once the request has been fully satisfied (readyState == 4, status == 200), the object's responseText() method will return all the text that the procedure printed with htp.p(), which typically would be built up from some database logic. If the output was a real XML document, you would retrieve it through the object's responseXML method.
Alternative Firefox shortcut for Find In Page
For a long time, I've used the Firefox option "Search for text when I start typing" to avoid having to type Ctrl-F / Command-F every time to find something on a page. But with more interactive Web 2.0-style pages perhaps using keyboard controls, now I've turned that option off.
However, you don't have to do Ctrl-F / Command-F to activate Firefox's find-in-page dialog. The / key also activates it; that's an easy mnemonic for me, because it is the same key that kicks off search mode in the vi editor. It's the best of all possible worlds -- using alpha keys as shortcuts in web apps, easy access to the Find dialog, and no carpal tunnel syndrome from multi-key "Find" shortcuts.
However, you don't have to do Ctrl-F / Command-F to activate Firefox's find-in-page dialog. The / key also activates it; that's an easy mnemonic for me, because it is the same key that kicks off search mode in the vi editor. It's the best of all possible worlds -- using alpha keys as shortcuts in web apps, easy access to the Find dialog, and no carpal tunnel syndrome from multi-key "Find" shortcuts.
Monday, November 26, 2007
Posting Reports from SQL*Plus
It used to be that all you needed to do with a SQL*Plus report was save it into a file, then you had everything you needed. (Or maybe just transfer the file to a web server afterwards.) These days, with blogs, wikis, etc., you might have to be a bit more creative.
For example, I was posting the results of a SQL*Plus report onto a wiki page. At the end of the SQL*Plus script, I had PROMPT commands to state the path of the report file and the URL of the wiki page. Then I'd copy and paste those items so I could open the report in an editor, open and edit the wiki page in a browser, and copy the contents of the file into the wiki edit box.
But if you're just doing the same thing every time, why not automate it? I hadn't made extensive use of the HOST command in SQL*Plus, so I held off a little longer than necessary. Here's the procedure I settled on:
Create a vim macro file that copies the contents of the edited file to the clipboard. (vim is a multiplatform vi clone with extra features like an extensive macro language, record/playback of keystrokes, and integration with features like the clipboard.) The procedure was something like:
vim -w clipboard.vi dummy_file.txt
[Then inside vim...]
"*yG
:q
That produces a macro file clipboard.vi with the keystrokes I did while editing the file. In this case, yanking the entire contents into the special buffer named *, which puts the results on the clipboard, then quitting.
Instead of just echoing the path of the report file, use the HOST command in SQL*Plus to run vim and load the report into the clipboard.
Instead of just echoing the URL of the wiki page, use the HOST command to open the default browser to that page, optionally using the URL that goes straight to the edit form.
-- Comment out the old PROMPT commands.
-- prompt Report is in: long_name_of_report.txt
-- prompt Wiki page is: http://...
-- Replace with fully automated HOST commands.
host vim -s clipboard.vi long_name_of_report.txt
host start http://...
Couple of wrinkles that I ran into along the way...
Although I use vim interchangeably between Windows and OS X, I'm finding that on OS X, the * buffer does not seem to replace the real contents of the clipboard; it gets retained somewhere between invocations of vim, but Cmd-V pastes the previous clipboard contents. Anyway, I use this technique primarily on Windows, where the * buffer really is the system clipboard.
On OS X, you would use 'host open' to launch the browser, as opposed to 'host start ' for Windows.
I had the Cygwin command START.EXE in my Windows %PATH% where it was being run instead of the default Windows START command. For some reason it didn't like being run via HOST, even though other Cygwin commands like LS worked fine. I renamed it to CYGSTART.EXE to keep it out of the way.
While diagnosing my problem with HOST START, I ran across a blog post by Tanel Poder, who describes the technique of spooling the report in HTML format and then using HOST START to open it in a browser.
When you're using the Wikimedia markup format (i.e. the same as used on the Wikipedia site), you might find that SQL*Plus reports map naturally to the Wikimedia table markup.
When I'm faced with automating a braindead simple editing job, or even a more complicated situation involving searching, moving, and replacing variable amounts of text, my first instinct is to look for a solution involving a vim macro, rather than writing a Perl program. I have vim on all the same platforms as I do Perl, and vim lets me try out the solution interactively and "debug" the sequence of edits more effectively than Perl does.
Sometimes, this approach is looked down on as not "real" programming. I'm curious, what's the most complex or important business process you've ever run as an editor macro (vim, emacs, or other editor of your choice)?
For example, I was posting the results of a SQL*Plus report onto a wiki page. At the end of the SQL*Plus script, I had PROMPT commands to state the path of the report file and the URL of the wiki page. Then I'd copy and paste those items so I could open the report in an editor, open and edit the wiki page in a browser, and copy the contents of the file into the wiki edit box.
But if you're just doing the same thing every time, why not automate it? I hadn't made extensive use of the HOST command in SQL*Plus, so I held off a little longer than necessary. Here's the procedure I settled on:
Create a vim macro file that copies the contents of the edited file to the clipboard. (vim is a multiplatform vi clone with extra features like an extensive macro language, record/playback of keystrokes, and integration with features like the clipboard.) The procedure was something like:
vim -w clipboard.vi dummy_file.txt
[Then inside vim...]
"*yG
:q
That produces a macro file clipboard.vi with the keystrokes I did while editing the file. In this case, yanking the entire contents into the special buffer named *, which puts the results on the clipboard, then quitting.
Instead of just echoing the path of the report file, use the HOST command in SQL*Plus to run vim and load the report into the clipboard.
Instead of just echoing the URL of the wiki page, use the HOST command to open the default browser to that page, optionally using the URL that goes straight to the edit form.
-- Comment out the old PROMPT commands.
-- prompt Report is in: long_name_of_report.txt
-- prompt Wiki page is: http://...
-- Replace with fully automated HOST commands.
host vim -s clipboard.vi long_name_of_report.txt
host start http://...
Couple of wrinkles that I ran into along the way...
Although I use vim interchangeably between Windows and OS X, I'm finding that on OS X, the * buffer does not seem to replace the real contents of the clipboard; it gets retained somewhere between invocations of vim, but Cmd-V pastes the previous clipboard contents. Anyway, I use this technique primarily on Windows, where the * buffer really is the system clipboard.
On OS X, you would use 'host open
I had the Cygwin command START.EXE in my Windows %PATH% where it was being run instead of the default Windows START command. For some reason it didn't like being run via HOST, even though other Cygwin commands like LS worked fine. I renamed it to CYGSTART.EXE to keep it out of the way.
While diagnosing my problem with HOST START, I ran across a blog post by Tanel Poder, who describes the technique of spooling the report in HTML format and then using HOST START
When you're using the Wikimedia markup format (i.e. the same as used on the Wikipedia site), you might find that SQL*Plus reports map naturally to the Wikimedia table markup.
When I'm faced with automating a braindead simple editing job, or even a more complicated situation involving searching, moving, and replacing variable amounts of text, my first instinct is to look for a solution involving a vim macro, rather than writing a Perl program. I have vim on all the same platforms as I do Perl, and vim lets me try out the solution interactively and "debug" the sequence of edits more effectively than Perl does.
Sometimes, this approach is looked down on as not "real" programming. I'm curious, what's the most complex or important business process you've ever run as an editor macro (vim, emacs, or other editor of your choice)?
Friday, November 16, 2007
OpenWorld at 20,000 Feet
I hadn't been to OpenWorld in a while until this year. Definitely a different tone than in years past. In the demo grounds, less "here are features we have in individual products", more "here are products built on top of those nifty features". Lots of demo booths from industry-specific acquisitions. Reminded me of IBM's SHARE conferences from the '90s, very customer focused.
Plus, Tom Kyte has a beard now. :-)
Plus, Tom Kyte has a beard now. :-)
Subscribe to:
Posts (Atom)
