Foxhound is the better* Database Monitor for SQL Anywhere.
*better: More thorough, more relevant, more effective.
...more Alerts, more All Clears, more details, more control in your hands.

[Home] [Back to Tip 75] [Forward to Tip 77] [Archives]

Breck Carter
Last modified: December 3, 1997
mail to:

Tip 76: Forensic Programming

(This Tip first appeared in the March 1995 issue of Pinnacle Publishing's PowerSource magazine.)

When does development end and maintenance begin? Some programmers say "I only like to work on new programs, maintenance is boring!" In actual fact, however, they are doing maintenance tasks whenever they're fixing a bug or making a change.

A better question is "How do I know when I'm doing maintenance?"

The answer is "You've started maintenance whenever you think your code is working." That point usually comes long before the program is done. For me, it comes every time PowerBuilder successfully saves a script or object. It can be argued that Rapid Application Development should really be called RAM because most of the time you're enhancing, fixing and changing things.

Every once in a while a real, industrial strength, "This is maintenance by anyone's definition" task comes along. 50,000 lines of PowerScript, 2000 stored procedures, 400 tables that someone else developed and you have to deal with. Maybe you have to convert the database from Oracle to Sybase or vice versa. Maybe you have to rip out the stored procedures, maybe you have to put them in. Maybe you just have to figure out why it doesn't work and of course it's going live next month.

I call this kind of task "Forensic Programming" because like Forensic Accounting and Forensic Entomology you're dealing with something after the fact. You're working with code, of course, not ledgers or insects, and the subject of your study might not be dead yet. But it will feel like it from time to time.

"Don't forget what you already know!"
- Coleman Sisson, Powersoft

Many of the techniques and approaches developed to deal with large pure-maintenance tasks can be put to use during development of new PowerBuilder systems. Some of them come from the mainframe environment where it's not uncommon to be faced with a million lines of undocumented code. As companies begin to develop large mission-critical PowerBuilder applications they will be faced with some of the same problems they had with COBOL. And many of the solutions are the same.

Regenerate, Regenerate, Regenerate

Organize your objects into libraries in a way that makes your job easier. The Library Painter is a great place to work from but not if you have to scroll through hundreds of objects to find what you're looking for.

One arrangement that works well is to place objects related to a particular business function in their own library. For example, if you're working on a function that uses one Window, one Menu and two DataWindows, put those 4 objects all by themselves in a single .PBL. Never mind that you're not going to ship the final system as several hundred tiny libraries, do what makes sense for the task at hand. You can combine and rearrange the libraries later.

Another arrangement is to split libraries by "things I've looked at, and ones I haven't." Or by object type: all the DataWindows here, all the Windows there, and so on. The Library Painter Entry - Move facility is ideal for this; don't us Entry - Copy unless you want to get really confused.

As you make changes do frequent regenerations of all the libraries. Regenerate twice, in fact, or three times if you're paranoid like me. Many subtle errors can creep into a large application that are only caught by either (a) regenerating all your objects or (b) a GPF.

Backup, Backup, Backup

Be especially careful when changing ancestor Menu objects. Each and every time you do that, open up the Menu Painter for each descendant. This step will point out changes to the ancestor that are causing problems for the descendants and they're usually easy to fix at this stage. Failure to check the descendant Menus soon after a change, however, can result in unusable objects with errors that are almost impossible to fix.

The same is true of other objects like inherited Windows and User Objects. Menus, however, are extremely sensitive and can bite without warning.

As you work, mistakes are inevitable. Most accidents with files are caused by human error rather than equipment failure. Last night's backup tape is nice to have but doesn't help you recover today's work after you accidentally delete the wrong script, or object, or whole library.

Make backup directories on the server or your workstation hard drive. If you're working in C:\SOURCE, make directories called C:\SOURCE.001, 002 etc. and copy everything frequently. Or use PKZip to make files like C:\BAK\SOURCE.Z01, Z02 and so on.

As time goes on, and disk space vanishes, you can delete old backups while keeping the recent ones. If you're converting a system from one technology to another you should probably keep the very first backup for a very long time just so you can answer those inevitable questions "How did they do that again? Ahh, NOW I understand why they did it that way!"

When starting a particular tricky change, use File - Save As to make a local backup of a single object. For example, when converting a DataWindow from using a SELECT to a stored procedure, save d_MyDW as d_MyDW_Save before getting down to the dangerous job of Export - Edit - Import (more on this later.)

After a while you may want to get these temporary objects out of your way. Rather than deleting them right away use Entry - Move to put them in OLDSTUFF.PBL. If you find an object that isn't being used, put it in UNUSED.PBL. Sure, you can delete these libraries later, but for the time being you can keep your libraries clean without worrying about zapping the wrong thing.

Get The Big Picture

The simple task of finding things is a real chore with a large PowerBuilder application. You don't have to be faced with 300 Global Variables to have a need to see all references to a particular thing. For example, a table or a stored procedure might have changed and you need to propagate that change throughout the system. Or you've discovered something that "isn't being done right" in a zillion places... how do you find things when the code is spread out in tiny chunks across thousands of scripts?

The Library Painter the Entry - Browse facility helps a bit. Click on the first object you want to search, and Shift+Click on the last one to highlight all the objects in between, then click on the little telescope toolbar button.

The Browse facility lets you launch the appropriate Painter for each object it finds that contains the search string. However, it just launches the object Painter and doesn't put you into the Script Painter for the particular control script among the dozens in that object. If your memory is like mine, it's a real drag trying to remember that we've got to find a (hidden?) DataWindow control called dw_Customer and then look in the RowFocusChanged event; by the time I get there I've forgotten what I was looking for.

The Browse facility also doesn't show much of the context (the rest of the script, etc.) containing the search string. It's difficult to switch between Browse and the object painters, and opening a second copy of PowerBuilder is out of the question. And Browse isn't particularly fast, at least not as fast a good text editor.

An alternative way to do global searches is to "flatten out" your entire application into a few ASCII text files or even a single huge text file and use a different tool from PowerBuilder. The ubiquitous Windows Write accessory can handle large files (see Figure 1), or you can use a full-tilt boogie product like Microsoft Word.

Figure 1: Searching Code With Windows Write

Creaky old DOS programs like Lotus Magellan can do blindingly fast searches across multiple files, and it can even look inside binary files. QEdit is one very popular fast text editor, but I haven't used it. My preference is the venerable mainframe look-alike SPF/PC simply because (a) it allows powerful sequences of exclude text and find text commands as shown in Figure 2, and (b) my fingers have been well trained to use it.

Figure 2: Exclude And Find With SPF/PC

PowerBuilder itself provides the mechanism for creating a text file version of your whole application: the LibraryDirectory and LibraryExport functions.

Given a library file name LibraryDirectory will return a list of all the objects it contains. LibraryExport can then be used to turn each object into a string containing the exported text for that object. This string contains carriage return and line feeds so with one FileWrite call the whole thing can be appended to an ASCII text file.

Repeat this for each library in your application, and each object in the library, and you've got the file displayed in Figures 1 and 2.

Figure 3 shows a little utility called LibAll that makes exporting a whole application a bit easier. LibAll uses your PB.INI file to find the libraries in the current application and exports them all to a single text file. It even chooses a name for this file (appname.txt) and a place to put it (same directory as the .PBL containing the application object) unless you say differently. And it lets you launch your favorite editor via the Edit button.

Figure 3: Exporting A Whole Application With LibAll

All the source and executable files for LibAll are on the PowerSource disk with this issue.

Same, Same, Same

The Export format takes a little getting used to, especially when it comes to DataWindows. The important thing to remember is that every single script in your application appears as-is in the Export format, albeit surrounded by new commands like on clicked and end on.

DataWindows are in a class by themselves. If you are familiar with dwModify and the dwSyntax program (DWSYN30.EXE) you will see all your favorite attributes buried among other things you've never heard of. The exported form of a DataWindow contains everything that's shown in the DataWindow Painter: every column, font, X/Y position and Validation Rule to name a few.

Figure 4: An Exported DataWindow

If you're very brave, and have followed the rule "Backup everything!" you can use an exported DataWindow to make some very difficult changes. Difficult, that is, to do with the DataWindow Painter.

Take this example: You have a complex DataWindow with lots of columns and fancy formatting and validation rules and you want to change it from using a Sybase SELECT to a stored procedure without repainting the thing from scratch.

Step 1 is to create the stored procedure to return the same fields of the same types in the same order as the original SELECT, using the same retrieval arguments in the same order. This is the "Same, Same, Same" rule for easy conversion of the DataWindow data source: Same number of fields, same names, same order, same datatypes, etc. If you want to change the fields, do it with the DataWindow Painter after you have successfully converted the data source.

Step 2 is to paint a throw-away DataWindow using that stored procedure. It doesn't matter what it looks like because you're only going to use the stored procedure call. Don't worry about validation rules, formatting or extra computed columns because all of that already exists in the original DataWindow.

Step 3 has you using the Library Painter to do Entry - Export on the two DataWindows. We will call the resulting files D_ORIG.SRD and D_TEMP.SRD for the original SELECT and throw-away procedure call DataWindows respectively. An example of the latter file is shown in Figure 4.

For Step 4 you can use your favorite text editor or even NotePad if the files aren't too big. Find the line in D_TEMP.SRD that starts with procedure="1 execute. Copy that single line over to D_ORIG.SRD and use it to replace the line that starts with retrieve="PBSELECT or retrieve="SELECT.

Step 5 and you're almost done: Use the Library Painter Entry - Import feature to import D_ORIG.SRD back into the correct library. Be sure to pick the right library since PowerBuilder defaults to the first one in your library list. Murphy's Law says that's never the one you want, and if you get it wrong there's always Entry - Move.

Step 6 is a reality check: Open up that DataWindow to see if everything's OK. It should work just like it did before except that the data source is now a stored procedure instead of a SELECT.

This technique has been suggested as a way to make use of PowerBuilder's extended dictionary PBCATxxx tables to create new DataWindows with Validation and Format rules automatically added, then change the data source to stored procedure. The six steps might sound complex but it's really just a simple copy and paste operation.

Resist the temptation to "fix other stuff" while you're editing the exported file. In particular, incorrect changes to id=n attributes on DataWindow column lines have been known to cause mass confusion: everything looks good in the DataWindow Painter but when you run the application the right data appears in the wrong columns.

[Home] [Back to Tip 75] [Forward to Tip 77] [Archives]
[mail to:]