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.


Breck Carter
Last modified: July 16, 1996
mail to: bcarter@bcarter.com



Beware The Shallow Copy

There was a rather serious mistake in the original edition of Transaction Management With Sybase. The good news is the error didn't have anything to do with the main argument of the article which was all about the evil nature of AutoCommit = False. And even better news is the fact the mistake points out a basic pitfall in PowerBuilder which we can all avoid once we understand it.

The original error appeared in Listing 3 where it showed how to save the current transaction object so the program could roll back the transaction and still have the original error information to display:

   txnSave = Create Transaction
   txnSave = SQLCA

At first glance that looks OK and it certainly compiles and executes without error. And if txnSave had been a structure all would have been well. But the problem is txnSave is an object, not a structure, and when you write things like "txnSave" and "SQLCA" you are not really referring to the object itself but merely to a pointer to the object.

At this point a true-blue PowerBuilder programmer should be completely confused because the word "pointer" means things like Hourglass! and Arrow! Unfortunately, in this context a pointer to an object means an indirect reference to that object. A pointer is one piece of computer memory which contains the address of the object which resides somewhere else in computer memory.

PowerBuilder does a pretty good job of hiding pointers from us, and that is a good thing because to manipulate pointers is to make mistakes. In the case of the assignment statement txnSave = SQLCA, however, the fact these are pointers to objects rather than the objects themselves makes this statement do something completely unexpected: the variable txnSave now points at the same object SQLCA points at. The original object that was created and assigned to txnSave is now lost in memory (a leak). It still exists but cannot be used because nothing points at it.

The initial effect of this assignment may seem exactly like a structure assignment because all the attributes are the same in the two objects. But whereas a structure assignment involves the a "deep copy" of all the data, no attributes are copied when pointers are assigned. Only a "shallow copy" is performed whereby the target pointer is changed to point at the same data as the source pointer. Later changes to one of these objects is immediately made to the other one.

In actual fact the "other object" is no longer available, just one object with two pointers pointing at it (sometimes called "reference variables" when PowerSpeakers get together.) Listing 1 shows the effect this has on object attribute values.

Listing 1: Colliding Attributes

   SQLCA.DBMS = "AAA"
   ltr_test = create transaction
   ltr_test.DBMS = "111"

   ltr_test = SQLCA  // two pointers, 1 object
   // ltr_test.DBMS now contains AAA; expected

   SQLCA.DBMS = "BBB"  // affects both fields
   // ltr_test.DBMS now = BBB: unexpected!

   //destroy ltr_test  // would destroy SQLCA!

Listing 1 also shows that an object-level operation such as the destroy statement affects both objects... again, there really is only one object available to be destroyed.

The solution is shown in Listing 2. When you actually want to make a copy of an object, you must code assignment statements for each attribute. These individual assignment statements work on basic datatypes like integer and string and actually copy data rather than simply manipulate pointers.

Listing 2: Safe to Assign Attributes

   SQLCA.DBMS = "AAA"
   ltr_test = create transaction
   ltr_test.DBMS = "111"

   ltr_test.DBMS = SQLCA.DBMS  // copy field
   // ltr_test.DBMS now contains AAA

   SQLCA.DBMS = "BBB"  // only affects SQLCA
   // ltr_test.DBMS still contains AAA

   destroy ltr_test  // doesn't touch SQLCA

Because PowerBuilder does not admit to the existence of these pointers, there is a basic semantic difficulty in PowerScript. For example, when you write SQLCA all by itself you mean "the pointer to the default transaction object." But when you write SQLCA.DBMS the word SQLCA now means something completely different: "the default transaction object itself."

This problem leads to something called a "characteristic error", a mistake programmers make that is characteristic of the tool they are using. In other words, they are led by the hand into making the mistake. Every programming language has its share of characteristic errors and it is up to us to learn how to avoid them rather than blame the tool.

The confusion with pointers extends to objects passed as arguments to functions. When you omit the "ref" keyword do not assume you are getting many of the benefits of the "pass by value" mechanism. All that is happening is that the pointer argument cannot be changed in the function.

In other words, objects passed by value cannot be changed to point at other objects. But all references to attributes within the object are honored. That means a window passed by value can have its title changed, and the destroy statement will make an argument unusable to the calling script.

The mistake of assigning object pointers instead of attributes can be easily avoided once you understand what is going on. The lack of a true "pass by value" mechanism for objects is more troubling, however. It goes beyond a simple characteristic error into the realm of deficiency. Currently there is no way to fully protect a caller's object arguments from change within a function and that would be a welcome feature.


Breck Carter can be reached by phone at (416) 763-5200 or via email at bcarter@bcarter.com.