SET things right
Global solutions to local problems are generally a bad idea. But just how bad they can really be? Andy and Marcia find out when they re-visit some old code. The effects of some commands like SET EXACT and SET UDFPARMS are well known, but some of the consequences of SET COMPATIBLE are more surprising and, one in particular, caused Andy a real problem very recently.
“I think I’ve found a bug in VFP 9.0”
Andy: I think I’ve found a bug in VFP 9.0, but I really can’t believe it because it seems so obvious that it could never have got through testing.
Marcia: What are the symptoms?
Andy: Well, I am calling one of my standard functions to perform a lookup using exact matching. This function saves the current work area (and some other relevant settings) and restores the work area before returning to the calling code.
Marcia: Hang on, why are you changing work areas anyway? You can just use the SEEK() function and reference the alias.
Andy: Ah, but the function checks to see if an index tag has been specified, and also whether a specified index tag actually exists, and if not, it uses a LOCATE instead of a SEEK(). As you know, you can’t do a “LOCATE IN ”. But that’s not the problem anyway. Here’s the relevant part of the code:
Local lnSelect
*** Save current work area
lnSelect = SELECT()
*** Restore work area
SELECT (lnSelect)
RETURN
Marcia: That looks pretty straightforward, so what’s the problem?
Andy: Well, when this function gets called from inside the application (by the way it’s an old FP2.6 app that is being converted to run under VFP 9.0) it returns to the wrong work area.
Marcia: What do you mean, it returns to the wrong work area?
Andy: Exactly what I say. If you call the code from work area 1, you are returned to work area 2 (which is empty)! Look at Figure 1 and you will see that the value of the variable “lnSelect” is showing 2, but the currently selected work area (as shown by the datasession window) is actually 1. I can’t find anything wrong and it is crashing the code. This is driving me crazy.
Marcia: Well that’s a short drive for you.
Andy: Thank you. Now please tell me what’s going on here. If I quit Visual FoxPro and re-start, and then do the exact same thing in the command window, everything is just fine. But when I’m running the app I get what you see in figure 1. It’s as if SELECT() is returning the current work area number + 1.

Fig. 1: It’s a bug! SELECT() = 2, but Work Area = 1
Marcia: Let’s approach this logically. If you are getting two different behaviors depending on whether you are running the application, or in the command window, then it is a racing certainty that something in the application environment is causing the problem.
Andy: Yes but what could be doing this. I have no idea where to start.
Marcia: I generally start by looking in the help file. Let’s see what it has to say about the SELECT() function since that is where you are seeing the problem. Maybe it’s changed in version 9.0.
Andy: The help file states that the SELECT() function returns either the number of the currently selected work area or the highest-numbered unused work. Passing a parameter of 0 gets the current work area and 1 returns the highest unused work area.
Marcia: It doesn’t say anything about passing no parameters at all does it? You aren’t actually passing either the 0 or the 1 in your code.
Andy: Ah, but zero is the default. You don’t need to pass it.
Marcia: No it isn’t. Where does it say that 0 is the default? All I can find is a reference in the Remarks which says that:
SELECT( ) returns the number of the current work area if SET COMPATIBLE is set to OFF. If SET COMPATIBLE is set to ON, SELECT( ) returns the number of the unused work area with the highest number.
Andy: Oh dear! I bet that old application has “SET COMPATIBLE = ON” somewhere in its startup.
Marcia: That would certainly explain what you are seeing. What happens if you use SELECT(0) in your function instead of just SELECT()?
Andy: It works! Checking the settings inside the function I find that COMPATIBLE is ON and code references (bless it!) shows that there is indeed a
SET COMPATIBLE DB4
in one the procedures called from the startup program (which is equivalent to SET COMPATIBLE ON)! So you’ve solved my problem and it’s not a bug in VFP 9.0 after all.
Marcia: Glad I could help. Interestingly there is a bug there, but it’s in the Help file not the code. Look at figure 2:

Fig. 2: The bug is in the Help File, not the code!
Andy: Yes, I see. When compatible is ON, the SELECT() function is returning the LOWEST free work area, number not the highest as stated in the help file. But I guess it doesn’t really hurt anything because it is still a free work area and you should never care what the exact number is anyway.
Marcia: Interestingly the behavior is identical in every version since VFP 6.0 (and maybe earlier), and the help file has said the same thing too. However, what really worries me is that this application should even be using the set compatible command. This is one of those commands that really does not have any place in a production application.
Andy: Why do you say that?
Marcia: Because it an example of a “Global Solution to a Local Problem” command.
Andy: What do you mean by global solution?
The trouble is that SET COMPATIBLE affects all sorts of things
Marcia: The trouble is that SET COMPATIBLE affects all sorts of things – not just the behavior of the SELECT() function (which has caused you to waste a couple of hours trying to find a non-existent bug). But there are a couple of other settings in Visual FoxPro that also illustrate what I mean. For example, when you want to do an exact match for a string comparison, you might consider including “EXACT = ON” in the code before the comparison. This is a global solution because it affects all string comparisons in the current datasession and unless you restore the prior state, you will affect not only the comparison you are about to do, but all subsequent operations.
Andy: I see what you mean. Debugging this could be a nightmare because unless you happen to hit the line of code that turns exact on you could run the application for days and never see a problem.
Marcia: Exactly. A much better solution would be to use the “==” operator to enforce the exact match. This is the correct local solution to a local problem.
Andy: Well, sort of. If you want to use the SEEK() function, you can’t use “==”. In fact that’s precisely why I created my own ExactSeek() function – the code sets exact on for the SEEK() and then restores it to its prior setting before returning control to the calling program.
Marcia: Yes, but what are you actually doing with this code? You’re localizing the solution by ensuring that the setting is only used for the execution of a single, self-contained, block of code. Perhaps another example will help. How about SET UDFPARMS?
Andy: Remind me. I’m not even sure what that setting is for.
Parameters can be passed either by reference or by value
Marcia: Parameters can be passed either by reference or by value. When a parameter is passed to a function or procedure by reference, any changes made to its value in the called code are reflected in the original value in the calling program. Conversely when a parameter is passed by value, the called code can change that value but the value in the calling program remains unchanged.
Andy: Yes, and I also know that Visual FoxPro interprets the code being called by the mechanism by which parameters are passed. So when the calling syntax looks like this:
luRetVal = CallMyFunction(param1, param2)
Visual FoxPro treats this as a Function Call and passes the parameters by value. However if the same code is called like this:
DO CallMyFunction WITH param1, param2
then Visual FoxPro treats this as a Procedure Call and passes the parameters by reference.
Marcia: Right. But it is actually the setting of UDFPARMS that defines how the parameters are passed when you call a block of code as a function. It’s just that the default happens to be set as “TO VALUE”. If you change the setting of udfparms then, even though you call the code as a function, the parameters get passed by reference.
Andy: Ouch! That could break a lot of code and would be really nasty to debug. Why would anyone do that because if you need to pass parameters by reference (as for an array) you can do so simply by prefixing the parameter name with “@”. In fact I didn’t even know there was another way to do it.

Fig. 3: The effect of SET UDFPARMS
Marcia: Now do you see what I mean by ‘global solutions to local problems’?
Andy: Definitely! But we’ve drifted off the point a bit. Let’s get back to SET COMPATIBLE. What else does that effect besides the SELECT() function?
But the ones that are relevant include some really nasty behavior changes
Marcia: The help file lists 32 entries (including one which merely says “Menu commands” but doesn’t actually tell us which ones). However, only about a dozen of them seem immediately applicable to Visual FoxPro – the others are really concerned with FoxPro 2.x issues. But the ones that are relevant include some really nasty behavior changes.
Andy: Odd, there are a lot of commands mentioned there that, when you go to the help file, make no reference to SET COMPATIBLE at all.
Marcia: Like?
Andy: APPEND MEMO, BROWSE, INKEY(), LASTKEY(), SET MESSAGE and SET PRINTER to name half a dozen. In my (admittedly simplistic) tests I can’t actually find any differences with any of them. I wonder why they are mentioned?
Marcia: Let’s deal with the ones that we know about for sure first. The one that really bothers me the most is the behavior of the DIMENSION and STORE commands when dealing with arrays.
Andy: I’ll bite. What’s the big deal here then?
Marcia: In Visual FoxPro re-dimensioning an existing array adds additional rows and columns but does not affect the existing contents, right?
Andy: Sure. Also I usually initialize my arrays to either empty strings, or zero to avoid issues with the default behavior which sets all elements to .F.. That’s easy using the STORE command.
Marcia: But both these behaviors depend on the setting of SET COMPATIBLE. Look at figures 4 and 5:

Fig. 4: SET COMPATIBLE affects re-sizing arrays

Fig. 5: SET COMPATIBLE also changes how STORE affects an array
Marcia: With COMPATIBLE = ON not only does re-dimensioning an array re-initialize it (whether you use DIMENSION or DECLARE makes no difference here) but using STORE deletes the array entirely and creates a single variable with the same name.
Andy: Oh that is bad! I have been using arrays extensively in this re-write and I confess that I haven’t noticed any odd behavior yet but this is something I really do have to check on – that behavior is just absurd.
Marcia: But presumably that’s how dBase was designed. After all, that is what compatibility mode is doing, making VFP behave like dBase.
Andy: I was just looking at FSIZE(). That’s another horror. Instead of returning the size of the specified field, it tries to return the size of a file with the name specified. The general result is that you get a “File is not found error”.
Marcia: Exactly, and here’s another one that will catch you out if you’re not careful. I know that in the data you are working on now there are character keys that are padded with spaces.
Andy: Yes, not my choice I have to say but…
Marcia: Does the Fox2.x code use the LIKE() function anywhere by any chance?
Andy: Yes, it does as a matter of fact. The first time I saw that function I had to go and look it up because I didn’t know what it did. Why do you ask that?
Marcia: Well, now we know why, or at least, can make a good guess at why, it uses COMPATIBLE = ON. Under this setting strings are trimmed before being compared by LIKE() as opposed to the normal Visual FoxPro behavior where trailing spaces are considered part of the values being compared.
Andy: Ah-ha! That explains something else I had noticed, but not really understood at the time. The original code used LIKE() inside a scan loop to select matching records but when I re-wrote the code to use a SQL query I got no results until I explicitly trimmed the key fields on which I was joining the tables. I hadn’t realized that it was only the setting of compatible that made the original code work.
Marcia: Insidious isn’t it? This is exactly the problems with using these global settings. Although solving one problem, they often cause unexpected problems in other, totally unrelated, areas.
I had always assumed that FoxPro and dBase were pretty much identical
Andy: What surprises me is the extent of the differences in behavior when they do arise. I had always assumed that FoxPro and dBase were pretty much identical, but clearly there were some pretty fundamental differences.
Marcia: Yet again it proves that our assumptions are often flawed. The rule, as always, when confronted with unexpected behavior is to check things out coolly and logically instead of immediately crying ‘bug’. And, of course, when all else fails, you can always read the fine manual.
Andy: Can’t argue with that!
Sources
De sources die bij dit artikel horen kun je downloaden via KramekAkins_SetThingsRight_SRC.zip.
(This article was first published in FoxTalk Magazine of April 2005)