SRP Editor – Metadata Structures
We recently introduced a new feature of the SRP Editor that we refer to as service module metadata, or simply metadata. To summarize, through the use of metadata records, the SRP Editor can provide enhanced tooltip and code complete features for stored procedures that are written to provide service-style (aka methods) options to developers. This is a productivity feature that SRP has been using for quite some time and we wanted to make it available to all users of the SRP Editor.
We were encouraged to get immediate positive feedback about this feature as well as strong interest in the metadata documentation so that this feature can be extended to additional stored procedures (including System Stored Procedures, or SSPs). This article will be the first of two that specifically deal with the creation of metadata records.
Today we will explore the metadata record structure itself. This will give you the groundwork for creating your own metadata records manually or, perhaps, for creating a nifty utility to do this for you. Our next article will document how you can add special comments to your own source code so the metadata records can be auto-generated. Regardless of the technique used, it all boils down to the metadata record structure. So let’s jump in.
Table and Key
Metadata records are stored in SYSENV. Keys have the following format:
<Proc> should be replaced with the stored procedure name. <App> should be replaced with the procedures application location, but it should be omitted entirely if the application is SYSPROG.
The following information is geared toward one specific type of metadata: the SERVICE type. Because the concept of metadata is very generic and flexible, we are allowing for other metadata types to be added. The specific type is indicated in the first attribute of the metadata record (see below). For now, when the term metadata is used, it is synonymous with SERVICE type metadata. When other types are released and documented, we’ll begin referring to them by their type more explicitly.
Here is the complete list of attributes (or fields) for the record, including the aforementioned type attribute:
<1> Type <2> Service Signature <3> Service Parameter Position <4> Generic Parameter Start Position <5> Services Quoted Flag <6> RESERVED <7> Services <8> Service Specific Parameters <9> Service Parameter Options <10> Option List Names <11> Option Lists <12> Option List Quoted Flags
As already noted, this must be set to SERVICE.
<2> Service Signature
This attribute contains an @SVM delimited list of parameter metadata. Collectively, all of the parameters being passed into a stored procedure are referred to as the signature. Each entry in this @SVM list should match the actual parameter name or it should be one of the predefined placeholders. Supported predefined placeholders are:
|@QUOTED_SERVICE||Indicates the parameter where the list of services should appear to the user. The list will appear only when the user types a quote character and the items in the list will be quoted.|
|@UNQUOTED_SERVICE||Same as above except the list items will not be quoted and the list will appear as soon as the user reaches the parameter.|
|@SERVICE||Same as @QUOTED_SERVICE.|
|@PARAMS||Indicates where the generic parameters for the service begins. This should always be the last parameter in the list.|
For example, the normal parameters of the Get_Property function are ObjectName, Property, and AuxiliaryArg. ObjectName and AuxiliaryArg are both parameters that have to be defined by the developer, so their actual parameter names will be used in the metadata. Property, however, can be identified as a pre-defined list of quoted options. Hence, attribute 2 of the metadata record would look something like this:
<2, 0, 1> CtrlEntID <2, 1, 2> @QUOTED_SERVICE <2, 1, 3> AuxiliaryArg
Note that in my above example I opted to use CtrlEntID rather than ObjectName. This is merely preference. Now, let’s look at the Rev_Utility function. It has the parameters Method, Param1, Param2…Param6. This is a classic service module signature. The Method parameter is clearly meant to be the place holder for one of many pre-defined methods (or services.) Param1 through Param6 are clearly meant to be generic parameters. Therefore, our metadata attribute would look like this:
<2, 1, 1> @QUOTED_SERVICE <2, 1, 2> @PARAMS
The use of @UNQUOTED_SERVICE is rare. Usually this will be used if the the list of services are pre-defined as equates within an insert.
<3> Service Parameter Position
The position of service placeholder parameter (i.e., @SERVICE, @QUOTED_SERVICE, or @UNQUOTED_SERVICE) within the aforementioned Service Signature attribute.
<4> Generic Parameter Start Position
The position of @PARAMS within the aforementioned Service Signature attribute.
<5> Services Quoted Flag
1 if the service placeholder parameter is quoted, 0 if not.
Currently unused. Leave blank.
This is a simple @VM delimited list of available services. It is recommended that this list be generated in alphabetical order.
The Rev_Utility function might have the following items in this attribute:
<7, 1> BORDERWIDTHS <7, 2> ERRORTEXT <7, 3> FSERROR <7, 4> GETSYSTEMFONTS <7, 5> SETREADONLY <7, 6> SETWINDOWSIZE <7, 7> SYSTEMSIZE <7, 8> TOKENIZE <7, 9> VERIFYPROC
<8> Service Specific Parameters
This is an associated multivalued list corresponding to each service within in the Services attribute (<7>). For each service in <7>, there should be an @SVM list of service-specific parameters here. Each parameter list contains only the parameters that replace the generic parameters. In other words, these are the parameters that replace the @PARAMS placeholder.
If you want to indicate optional parameters, place an opening square bracket before the first optional parameter and a closing square bracket after the last parameter. This will cause SRP Editor to display these parameters in italics.
Continuing with our Rev_Utility function example, here’s what might appear in this attribute:
<8, 1, 1> WindowName <8, 2, 1> ErrorCode <8, 3, 1> <8, 4, 1> <8, 5, 1> ControlList <8, 5, 2> ReadOnly <8, 6, 1> WindowName <8, 6, 2> SizeArray <8, 7, 1> <8, 8, 1> String <8, 9, 1> StoredProcedureName
<9> Service Parameter Options
This list parallels the Service Specific Parameters attribute (<8>). For each parameter in <8>, if there is a list of options for it, then the name of the list must appear here in the exact same position. These names will appear and be defined in attributes <10>, <11>, and <12>.
For example, let’s say there is a service called “NEW”, which appears in position <7,8>. Field 7 is the list of services, and this is the 8th service. Therefore, the parameters for the “NEW” service must appear in <8,8>.
Let’s say there are two parameters for this service: Type and NewValue. This would place Type into position <8,8,1> and NewValue into position <8,8,2> of the record, since service parameters are @SVM delimited. So far, so good.
Now, let’s say the Type parameter wants to show an option list, which has a unique name. In this example, we’ll the list name will be TYPES. Since the Type parameter is in position <8,8,2> of the record, then we need to have TYPES appear in <9,8,2>, the position exactly parallel to the parameter. Now the SRP Editor knows to display the list named TYPES as soon as it reaches the Type parameter, but only if the current service is “NEW”.
For the Rev_Utility function, the SetReadOnly method has a parameter called ReadOnly. According to the documentation, ReadOnly accepts a boolean value. Therefore, we could create a BOOLEAN option list name and enter this in the appropriate position. Referencing the Service Specific Parameters sample attribute above, the ReadOnly parameters appears in <8,5,2>. Hence, this is how attribute <9> should look:
<9, 1, 1> <9, 2, 1> <9, 3, 1> <9, 4, 1> <9, 5, 1> <9, 5, 2> BOOLEAN
<10> Option List Names
Option lists are defined separately. Each list has a unique name, which is stored as an @VM list here, and should be stored alphabetically for quicker searches. By defining option lists by name, the option lists can be associated to more than one parameter. For example, you might have three lists called TYPES, SIMPLETYPES, and STYLES. Option names should be in all-caps as they are used as keys internally by the SRP Editor. These names will never be displayed to the user. Referring to our ongoing Rev_Utility metadata example, the word BOOLEAN would appear in this list.
<11> Option Lists
This is an associated multi-valued list to the Option List Names attribute (<10>). For each name in <10>, there should be an @SVM delimited list of values for that list. Each option list should each be alphabetized for a better user experience, but note that the SRP Editor will display them as they are defined here.
Our BOOLEAN option list name has two possible values. Because almost every stored procedure uses the LOGICAL insert or a custom one that includes the False$ and True$ equates, our attribute <11> could look like this:
<11, 1, 1> False$ <11, 1, 2> True$
Note, the above example assumes that BOOLEAN is stored in <10,1>.
<12> Option List Quoted Flags
This is an associated multi-valued list to the Option List Names attribute (<10>). For each name in <10>, there should be either a 1 indicating that the option list items are to be quoted or 0 indicating otherwise. Since the BOOLEAN options are equated variables, these should be considered unquoted parameters (and thus a 0 would appear in <12,1>).
To bring this all together, here is what our Rev_Utility metadata record now looks like:
<0> SRP_EDITOR_METADATA*REV_UTILITY <1> SERVICE <2, 1, 1> @QUOTED_SERVICE <2, 1, 2> @PARAMS <3> 1 <4> 2 <5> 1 <6> <7, 1> BORDERWIDTHS <7, 2> ERRORTEXT <7, 3> FSERROR <7, 4> GETSYSTEMFONTS <7, 5> SETREADONLY <7, 6> SETWINDOWSIZE <7, 7> SYSTEMSIZE <7, 8> TOKENIZE <7, 9> VERIFYPROC <8, 1, 1> WindowName <8, 2, 1> ErrorCode <8, 3, 1> <8, 4, 1> <8, 5, 1> ControlList <8, 5, 2> ReadOnly <8, 6, 1> WindowName <8, 6, 2> SizeArray <8, 7, 1> <8, 8, 1> String <8, 9, 1> StoredProcedureName <9, 1, 1> <9, 2, 1> <9, 3, 1> <9, 4, 1> <9, 5, 1> <9, 5, 2> BOOLEAN <10> BOOLEAN <11, 1, 1> False$ <11, 1, 2> True$ <12> 0
As you can see, there is a fair amount of complexity in the metadata structure. This is why we recommend generating metadata using a tool rather than rolling it out by hand. However, once one metadata record has been created, it is relatively easy to copy it into a new metadata record and adjust as needed for the specific stored procedure. As already mentioned above, our next article in this series will show you how to comment your own code so the metadata records can be generated automatically by the SRP Editor.