APPENDIX A Temporary String Space (TSS)


by Loren West

My understanding of how Informix handles Temporary String Space has increased
dramatically over the last couple of months, and I'd like to share that
information for anyone (everyone) that has ever had a string space problem.

1) TSS in Informix is limted. It is only about 4K or so.

2) Very few things require TSS. Usually, those things use it, then clear the
entire TSS when they're done. In my opinion, Informix's 4K limit on TSS is
adequate. It only becomes a problem under certain (and completely avoidable)
circumstances.

3) TSS is used in the following scenarios:

   A) Passing strings to, and returning strings from functions.

   B) Internal scratchpad usage within certain 4GL commands.

* Scenario (A) is blamed for 4GL running out of temporary string space. It
also, usually, is not the source of the problem.

4) In order to understand the source of the problem, it's important to
understand how temp string space is used, and when it's cleared.

When a string is passed to a 4GL function, the calling function places it into
TSS. IMMEDIATELY upon invocation, the called function retrieves it into a
local variable, and (and this is the key) if there is nothing else in TSS, the
ENTIRE TSS is cleared and reset.

IF there is something else in TSS, it waits until that something is used, THEN
it resets TSS. IF that something never uses it, then you get the out of string
space problem.

The following example shows normal TSS usage:

  1 main
  2 call myfunct("Hello")
  3 end main
  4
  5 function myfunct(str_var)
  6 define
  7 str_var char(20)
  8 display str_var clipped, " World"
  9 end function

In the above example, line 2 places the string "Hello" into TSS. Line 7
actually retrieves "Hello" from TSS, puts it into the local str_var variable,
and because there is nothing else in TSS, it is cleared and reset.

Line 8 puts str_var into TSS, then puts " World" into TSS, then calls (an
internal 4GL) display function. This display function retrieves "Hello" from
TSS, then retrieves " World" from TSS, then (because there is nothing else on
the stack) clears and resets TSS. It then goes about it's business of
displaying "Hello World" onto the screen.

The following example shows how you can get into trouble with TSS:

  1 main
  2 define
  3 local_var char(20)
  4 let local_var = "Hello", myfunct(" World")
  5 end main
  6
  7 function myfunct(str_var)
  8 define
  9 str_var char(20)
 10 return str_var
 11 end function

In line 4, the "Hello" string is put into TSS (for the `let' command), then
" World" is placed into TSS for the function call to myfunct().

The difference here is line 9. When " World" is removed from TSS, it is not
cleared because "Hello" is still there. In line 10, TSS is used again to
return the string " World" back to the calling function. At this point, TSS
contains the following:
			"Hello"
                        " World"
			" World"

When myfunct() returns, " World" is taken from the TSS, then put back into the
TSS for the let command to process. At this time, TSS looks like:
			"Hello"
                        " World"
			" World"
			" World"

Finally, the let command gets the " World", then the "Hello", and when it gets
the "Hello", the TSS is cleared.  This is a normal situation. It uses TSS, but
not much, and it is appropriately cleared after the `let' command is done
processing.

The problem that can arise using this syntax is if the myfunct() command
doesn't return right away. If myfunct() calls alot of different function, and
if those functions consume any TSS, then it will not be reset until the first
`let' command has finished processing. You're very likely to get a string space
error in this scenario.

5) The following scenarios will result in an out of string space error if the
function that is called is a "string hog" (a string hog function is one that
uses TSS alot - not necessarily one that uses alot of TSS, but one that uses
TSS alot):

 A) Calling a function from within a string concatenation"
     This will error:
	 let a_string="Hello",string_hog()

     This will not:
         let temp_string=myfunct()
	 let a_string="String",temp_str

 B) Using the `case' command with a string argument.
     This will error:
	 case a_string
	   when "A"
	     display "OK"
	   when "B"
	     call string_hog()
	 end case

     This will not:
         case
	   when a_string="A"
	     display "OK"
	   when a_string="B"
             call string_hog()
	 end case

Note also that the `using' keyword can be categorized as a `string hog'
function. This doesn't mean that it inappropriately uses TSS, it just uses
alot.

6) In most cases, finding the source of the string space error is incredibly
difficult. It usually is caused in a function far away from the place that
the program actually fails.

What follows is a function that is guaranteed to work if TSS is clear, and is
guaranteed to fail is TSS has something in it. Since there is no way of
printing out the TSS, I suggest setting breakpoints in your program (using the
debugger), and calling this function from the debugger command line.

If you get the string space error when you call this function, then there
definately is something in TSS. If you don't get an error, then there is
definately nothing in TSS. Once you get the error, keep running the program
the same way, (calling this function from the debugger command line) and
setting breakpoints earlier and earlier. Using this method, you should be
able to find the place in the program that uses TSS without resetting it. In
most cases, the fix is to write the chunk of code in a different way.

You might find that the error occurs in a C function that you have written. It
is very easy to write a C function that doesn't use TSS properly. Because of
this fact, also don't be surprised if a 4GL command used in a non-standard
way causes this result. No programmers are perfect - Informix may have a bug
in one of their commands if used in a non-standard way. Once found, simply
write the block of code a different way.

If you are getting "out of temporary string space" errors, put the following
functions in your main.4gl and call tss_hog() from the debugger command line
(as specified above):

	function tss_hog()
	  define n smallint, str_var char(50)
	  for n=1 to 1000
	    let str_var=tss_hog1()
	  end for
	end function

	function tss_hog1()
	  return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
	end function

If you don't have the debugger, you can put calls to tss_hog() in strategic
places in your program, then recompile and test narrowing the gap between a
call to tss_hog() that works and the call to tss_hog that fails.

Once you've narrowed it down to one line in your program where, if you call
tss_hog() before executing that line, it works and if you call tss_hog()
after executing that line, it fails - you've found the offensive line.

If the syntax of that line doesn't match one of the above "don't do" scenarios,
please let me know. I'll add that scenario to the list next time this article
is published.