The %FIELDS% Record (TB#95) (Functions/Subroutines/Programs)
Created at 14 NOV 1996 02:02PM
The %FIELDS% Record
The dictionary of each file contains, as part of its control information, a record called %FIELDS%. The %FIELDS% record contains a summary of the most important characteristics of field definitions in that dictionary. This technical bulletin provides information on the layout and use of this control record.
%FIELDS% Layout
The layout of the %FIELDS% record is:
Pos Description
1 Reserved
2 Sequential counter for next position default in Dictionary window.
3 Field names in ascending left-justified order.
4 Type (F or S).
5 Position.
6 Btree flags. True if this field is Btree indexed.
7 Cross-reference flags. True if this field is Cross-reference indexed.
8 Key part.
9 Single or Multivalued.
10 Justification.
11 Display length.
12 Output format.
13 Default value.
14 Master flag. True if this is the "master" definition of a field (not a synonym).
Fields 3 through 14 are associated multivalued fields, with field 3 (field names) as the controlling field.
Maintaining %FIELDS%
The %FIELDS% control record is maintained automatically by DICT.MFS. Each time you write a record to the dictionary, DICT.MFS examines the record and determines whether it is a valid field. A field valid for inclusion in %FIELDS% must have these characteristics:
1) It is an F-, G-, or S-type field.
2) The field name does not begin with the characters "%" or "@".
3) The field has compiled successfully (F- or S-type).
4) The field could be written successfully to the dictionary.
5) If F-type, it has a numeric Position.
6) If S-type, there is a formula.
7) If G-type, there is display information in attribute 3 of the field.
The fact that DICT.MFS enforces certain naming conventions (no "%" or "@" beginning the field name) means that these characters can only be used as prefixes in the names of dictionary control records (for example, "%SK%"). Other field name prefixes are also restricted (no "$" as a prefix, for example); although DICT.MFS does not prohibit these, other higher-level processes will fail on these types of field names.
If the %FIELDS% record does not exist, DICT.MFS builds it when first opening the dictionary. If this is happening, you will see the message "Rebuilding control records …".
If the record exists at all, DICT.MFS does not synchronize it with the existing dictionary. This leads to the possibility that %FIELDS% can be logically corrupt. For instance, this could happen if %FIELDS% were deleted, but then new dictionary records were added before DICT.MFS had an opportunity to re-open the file.
If you suspect that %FIELDS% is not up-to-date, delete the record, then re-attach the file and list the dictionary. This will cause DICT.MFS to rebuild the record. If you should ever delete the %FIELDS% record you should immediately re-attach the file.
You cannot update the %FIELDS% record yourself because it is write-protected by DICT.MFS. You will see an error to this effect if you attempt to write the record in any way.
The fact that all valid field names are maintained in %FIELDS% creates an implied limit to the size of a dictionary. All summary information about a dictionary, including all the details listed above, must fit within 64K. However, this limit is comfortably high. Assuming for a moment an average of about 35 bytes per dictionary entry, the maximum allowed would be around 1,875 fields per dictionary. Unusually long field names, formatting specifications, or default specifications would, of course, bring this number down.
Using %FIELDS%
The fact that a summary of the dictionary is maintained in a single record can come in handy during application development, mostly in the aid of speed. Examples of uses for %FIELDS% include:
_ Creating quick pre-sorted listings of field names.
_ Deriving field positions quickly.
_ Deriving formatting specifications for reports.
_ Checking index flags quickly.
Examples of different uses appear in the Figures.
Figure 1
* sorted field names in a popup
DECLARE FUNCTION POP.UP
FILENAME = 'SAMPLE_CUSTOMERS'
AT_FIELDS = XLATE('DICT.':FILENAME,'%FIELDS%',,'X')
FIELD_NAMES = AT_FIELDS<3>
CONVERT @VM TO @FM IN FIELD_NAMES
X = 0
Y = 0
FILE = "@":FILENAME
LIST = FIELD_NAMES
FORMAT = "1:25:L::Field Name" ; * 1 fm-delim element per column
MODE = "R"
SELECT = 1 ; * single selection popup
TITLE = FILENAME: " fields"
ATTRIB = ""
HELP = ""
COORD = "1" ; * return col 1 if TYPE is null
TYPE = "" ; * if null, return column in COORD
@ANS=POP.UP(X,Y,FILE,LIST,FORMAT,MODE,SELECT,TITLE,ATTRIB,HELP,C OORD,TYPE)
Figure 2
* use %FIELDS% to validate field names and get formatting information for a report
FILENAME = 'SAMPLE_CUSTOMERS'
OPEN FILENAME TO FILE ELSE CALL FSMSG() ; STOP
OPEN 'DICT.':FILENAME TO @DICT ELSE CALL FSMSG() ; STOP
READ AT_FIELDS FROM @DICT,'%FIELDS%' ELSE CALL FSMSG() ; STOP
FIELD_NAMES = AT_FIELDS<3>
JUSTS = AT_FIELDS<10>
LENGTHS = AT_FIELDS<11>
* build table of fields to be displayed and their corresponding output formats
DISPLAY_FIELDS =
DISPLAY_FORMATS =
FIELD_CNT = 0
LOOP
NEXT_FIELD =
CALL MSG( 'Next field to display?','RC',NEXT_FIELD,)
WHILE NEXT_FIELD
* validate entry
LOCATE NEXT_FIELD IN FIELD_NAMES USING @VM SETTING POS THEN
DISPLAY_FIELDS←1> = NEXT_FIELD
DISPLAY_FORMATS←1> = JUSTS<1,POS> : '#' : LENGTHS<1,POS>
FIELD_CNT += 1
END ELSE
TEXT = NEXT_FIELD: " is not a valid field name!"
CALL MSG(TEXT,
,,
)
END
REPEAT
HEADING "Report of the file: ":FILENAME: "'L'"
SELECT FILE
DONE = 0
LOOP
READNEXT @ID ELSE DONE = 1
UNTIL DONE
READ @RECORD FROM FILE, @ID THEN
FOR CTR = 1 TO FIELD_CNT
PRINT CALCULATE( DISPLAY_FIELDS<CTR> ) DISPLAY_FORMATS< CTR > :
NEXT CTR
END
REPEAT
Figure 3
* simple listindex (does not include relationals)
FILENAME = 'SAMPLE_CUSTOMERS'
AT_FIELDS = XLATE('DICT.':FILENAME,'%FIELDS%',,'X')
INDEXED_FIELDS =
COL = 0 ; CTR = 1
LOOP
REMOVE NEXT_FIELD FROM AT_FIELDS<3> AT COL SETTING FLAG
IF AT_FIELDS<6,CTR> OR AT_FIELDS<7,CTR> THEN
INDEXED_FIELDS := NEXT_FIELD : @FM
END
CTR += 1
WHILE FLAG REPEAT
IF INDEXED_FIELDS THEN
INDEXED_FIELDS[-1,1] = ; * strip trailing field mark
TEXT = 'Btree/Crossref indexes in ':FILENAME
TEXT←1> = INDEXED_FIELDS
CALL MSG( TEXT,
,,
)
END ELSE
TEXT = 'No fields are indexed in ':FILENAME
END