The format for these macros will be:
/*#IFDEF(MACROCLASS) [Commented code here] #ENDIF#*/
In this case, the 'MACROCLASS' represents what will be known in the framework as a "MacroClass", to be defined in a table called MacroClasses. These can be enabled or disabled and comments will be expanded appropriately according to those settings.
The "[Commented code here]" segment represents any valid TSQL code block. For instance, if a developer wanted to embed a SELECT that should only be run in an envrionment in which the "ABC" MacroClass was enabled, the following code block would be used:
/*#IFDEF(ABC) SELECT SomeColumn FROM SomeTable #ENDIF#*/
A macro called "ABC" can be defined with an expansion of "SELECT 1". Everywhere the preprocessor finds the text "ABC", a "SELECT 1" will be substituted.
Parameterized macros are also available. A macro called "SELECTFROMTABLE" can be defined with parameters "TABLE" and "COLUMN". The expansion text for this might be "SELECT B FROM A". If the macro is called using parameters, e.g.: SELECTFROMTABLE('TableName', 'ColumnName'), the macro will be expanded as "SELECT ColumnName FROM TableName".
The format for using inline parameterized macros is:
MACRO_NAME represents a macro as defined in a table called Macros. Each macro belongs to a MacroClass and can be disabled independently of the MacroClass -- so if a Macro is a member of an enabled MacroClass, it can still be disabled without disabling the other Macros or the class itself. If the class itself is disabled, the enabled setting for the Macro itself is ignored.
PARAMLIST represents the macro's parameter list, which is a comma-delimited list of 0 or more string values or TSQL variable names to be substituted in the expansion text defined for the clause. For instance, values like 527, '1, 2, 3', 'A, B, C', ''A', 'B', 'C'', 'SELECT * FROM MyTable', and @Variable are all valid parameters. Note that strings like '1, 2, 3' or 'SELECT * FROM MyTable' will be substituted directly -- without their quotation marks. However, strings with substrings, e.g. ''A', 'B', 'C'', will keep their inner quotation marks and will expand to a list of three strings ('A', 'B', and 'C').
Parameters in the defined parameter list and expansion text will be #-delimited strings of alphanumeric characters or the underscore character. So the following parameters are legal: #ABC#, #A#, #MY_VALUE#. Parameters can be directly substituted at any point in the substitution text. For instance, the actual definition for the SELECTFROMTABLE macro described above would be:
The expansion text would be:
"SELECT #COLUMN# FROM #TABLE#"
/*#IFDEF(MACROCLASS) --#MACRONAME(PARAMLIST)# #ENDIF#*/
Note that this type of macro will only be expanded if it is nested within a multiline macro that is also expanded. So if, in this case, the "MACROCLASS" macro class is not enabled, the "MACRONAME" macro will not be expanded.
This type of macro will be converted into normal inline style when it is expanded.
So the following macro:
/*#IFDEF(ABC) SELECT SomeColumn FROM SomeTable #ENDIF#*/
Will be expanded to something like:
/*#IFDEF(ABC) E(8466DA48-31A7-4606-97E7-69124AB21084)#*/ SELECT SomeColumn FROM SomeTable /*#E(8466DA48-31A7-4606-97E7-69124AB21084) #ENDIF#*/
Note that a GUID is inserted in the start and end comments; this guid is present in order to make breakdown, verification, and management of nested macros easier.
Also note that the macro syntax is quite rigid; a macro that does not meet the exact requirements will simply be ignored by the preprocessor. An IFDEF for ABC, for instance, must follow the format:
No additional whitespace is permitted. A matching, properly formatted ENDIF must be present as well, or no expansion will occur. All macros are to be stringently validated in order to ensure that non-macro comments are not expanded.
Inline expansion is somewhat more complex as it involves parameter substitution as well as comment matching. The example from above, SELECTFROMTABLE, formatted as:
Will expand to:
/*#SELECTFROMTABLE('MyTable', 'MyColumn')# X(2BEFAA7D-EDCE-4228-AC6B-F7BD25C16CB6)*/SELECT MyColumn -> -> FROMMyTable/*#Y(2BEFAA7D-EDCE-4228-AC6B-F7BD25C16CB6)#*/-> represents a line continuation.
Note again the presence of the GUID for verification purposes -- future versions of TSQLMacro may support nested inline macros, much like macros in other programming languages. This GUID should make addition of that feature quite simple. Also note that the expansion is "stuffed" in; therefore, had the original macro had a comment or some TSQL on the same line before or after, it would have been unaffected. For instance:
SELECT * FROM ATable /*#SELECTFROMTABLE('MyTable', 'MyColumn')#*/ SELECT * FROM AnotherTable
After expansion, three SELECT statements will be available -- one of the primary TSQLMacro design goals is to affect surrounding code and comment blocks as little as possible.
No macros that are commented out by non-macro comments will be touched by the preprocessor -- they will neither be expanded nor collapsed. So the following will never be touched:
Inline macros using the nested syntax (for nesting within multiline comments) will be converted to standard inline comments for expansion. For instance:
/*#IFDEF(DEBUG) --#SELECTFROMTABLE('MyTable', 'MyColumn')# #ENDIF#*/
Assuming that both the DEBUG macro class and SELECTFROMTABLE macros are enabled, the expansion of this block might look like:
/*#IFDEF(ABC) E(39D18F9C-7A20-4ABB-87C6-3DEAB085849D)#*/ /*#SELECTFROMTABLE('MyTable', 'MyColumn')# X(FCC6BDDE-827E-4611-A7B2-2BC9B0108A83)*/SELECT MyColumn -> -> FROM MyTable/*#Y(FCC6BDDE-827E-4611-A7B2-2BC9B0108A83)#*/ /*#E(39D18F9C-7A20-4ABB-87C6-3DEAB085849D) #ENDIF#*/
Once expansion is completed, the preprocessor will automatically re-apply the routine (using the ALTER syntax). It's assumed that the preprocessor will always be run by users with access to alter routines. If there is a need, an access check can be added to a future version.