Go backward to Uses of m4 Macros
Go up to Top
Go forward to Characteristic Functions

Global Constants

Transact-SQL does not provide the programmer with the ability to define any kind of global constructs. Macros can be used to define global constants. These constants are then reusable in many situations. For example, return codes can be used in stored procedure definitions to allow the user to interpret what happened when a stored procedure has been executed.
      define(_HELP,1000)
      define(_INSERT,2000)
      define(_UPDATE,3000)
      define(_DELETE,4000)

      define(_retcode,`eval($1)')
These macro definitions together with stored procedure return statements such as
      return _retcode(_INSERT)
which expands to
      return 2000
allow us to use the same return codes across all stored procedures without having to remember what they are. Just remember to include(macro_def.m). In the above macro definition we use the predefined eval() macro which attempts to interpret its argument as an arithmetic expression and then evaluates to the value of that expression. We can also add specifics to the _retcode macro such as consecutively numbering each use of the macro in a particular stored procedure.
      define(_location, 0)  /* executed once at the beginning of the 
                               procedure */

      define(_retcode,`eval(ifelse($1,,,$1 +)  _location) 
                       define(`_location',eval(_location + 1))')
This macro is made up of two components - eval(ifelse($1,,,$1 +) _location) and define(`_location',eval(_location + 1)). The first component uses the current value of _location in defining the actual return code. The ifelse(a,b,c,d) built-in macro works as follows: if a and b, once evaluated, are identical then ifelse returns the evaluation of c; otherwise it evaluates to the value of d. Hence the ifelse($1,,,$1) macro starts by evaluating $1. If it is NULL (ie, equal to the second (missing) argument in the ifelse macro) the macro returns nothing (ie, the third (missing) argument); otherwise the macro returns the evaluation of $1 + (the fourth argument). As a result the first component produces either eval($1 + _location) or eval(_location) depending on whether an argument is passed to the _retcode macro or not, respectively. Of course, this must be evaluated further to produce an actual number.

The second component, define(`_location',eval(_location + 1)), is a bit more subtle. Recall that the macro _location has just been used to produce an actual return code. Now we seem to be defining _location within another macro. This is exactly what is happening. Indeed, _location is being defined to be 1 greater than its previous value. This ensures that the next time the _retcode macro is used, even with the same argument, say _INSERT, it will produce a different value - one greater than its previous value. What really allows this to happen are the quotes around the _location macro being defined which prevent the _location macro from being evaluated immediately. Otherwise something like define(5, eval(5 + 1)) would be encountered by m4 and it would complain.

Thus the SQL/m4 code

        ...   /* SQL code */
      return _retcode(_INSERT)
        ...   /* more SQL code */
      return _retcode(_INSERT)
        ...   /* still more SQL code */
which repeatedly uses _retcode with the argument _INSERT in a stored procedure will generate the SQL code
        ...   /* SQL code */
      return 2000
        ...   /* more SQL code */
      return 2001
        ...   /* still more SQL code */
when processed by m4.
pletch@mcs.newpaltz.edu

Prev Up Next