Metadata Comments
Metadata comments are the Tripous syntax used by the Registration Builder inside Schema.sql files.
They let a SQL table definition also describe application declarations.
The Registration Builder parses those comments and generates C# registration code.
Comment Types
There are two kinds of metadata comments:
- Header metadata comments.
- Field metadata comments.
Header metadata appears before a CREATE TABLE statement.
Field metadata appears after a field definition.
/*---------------------------------------------------
Table: Product
Module: Product ProductDataModule
Group: Inventory
Form: Product ProductForm
FilterFields: Code, Name
IsLookup
----------------------------------------------------*/
CREATE TABLE {TableName} (
Id @NVARCHAR(40) @NOT_NULL primary key,
Code @NVARCHAR(40) @NOT_NULL, -- Code PRD-XXXX
Name @NVARCHAR(96) @NOT_NULL,
Notes @NBLOB_TEXT @NULL -- LargeMemo; Group Notes
)
The header describes table and module intent.
The field comments describe field-level behavior.
Top Table Rule
A module has one and only one top table.
Only the top table declares module-level metadata.
Examples:
ModuleGroupFormItemPageDetailOrderCodeListWhereFilterFields
Detail and subdetail tables do not declare module metadata unless they are also exposed as separate top-level modules.
This keeps module ownership clear.
Header Syntax
General header shape:
/*---------------------------------------------------
Table: TABLE_NAME
Module: Default | MODULE_NAME [MODULE_CLASS_NAME]
Group: GROUP_NAME
Form: DataForm | FORM_NAME [FORM_CLASS_NAME]
ItemPage: ItemPage | ITEM_PAGE_CLASS_NAME
DetailOrder: PARENT_TABLE_NAME=DETAIL_TABLE_NAME, DETAIL_TABLE_NAME
Code: Code [Draft] [Pattern] [ProviderName]
ListWhere: SQL_CONDITION
FilterFields: Field1, Field2, FieldN
FieldGroups: Address, Billing, Notes
IsLookup
NotUiVisible
IsReadOnly
IsSingleSelect
NoFilters
NoCascadeDeletes
NoGuidOids
-----------------------------------------------------
free text comments
----------------------------------------------------*/
Table is always required.
Table must be the first metadata entry.
If the table is a top table, Table is followed by one or more module blocks.
Table-level metadata such as FieldGroups and boolean flags follows the module blocks.
Module Blocks
Each Module line starts a module block.
The following entries belong to that module block until the next Module line or until non-module header metadata begins.
Module block order:
ModuleGroupFormItemPageDetailOrderCodeListWhereFilterFields
Module and Group are required for each module block.
The other entries are optional.
Multiple Modules On One Table
A single top table may declare multiple modules.
This is useful when the same physical table stores multiple document types.
Example:
/*---------------------------------------------------
Table: Trade
Module: SalesOrder SalesOrderDataModule
Group: Sales
Form: SalesOrder SalesOrderForm
ItemPage: TradeItemPage
DetailOrder: Trade=TradeLine, TradeTax
DetailOrder: TradeLine=TradeLineTax
Code: Draft SO-YYYY-XXXXXX
ListWhere: DocumentType.ModuleName = 'SalesOrder'
FilterFields: Code, TradeDate, Person__Name, TradeStatus, TotalAmount
Module: SalesInvoice SalesInvoiceDataModule
Group: Sales
Form: SalesInvoice SalesInvoiceForm
ItemPage: TradeItemPage
DetailOrder: Trade=TradeLine, TradeTax
DetailOrder: TradeLine=TradeLineTax
Code: Draft SINV-YYYY-XXXXXX
ListWhere: DocumentType.ModuleName = 'SalesInvoice'
FilterFields: Code, TradeDate, Person__Name, TradeStatus, TotalAmount
FieldGroups: Totals, Billing, Shipping, Organization, Relations, Audit, Notes
----------------------------------------------------*/
Each module gets its own generated ModuleDef, list SQL, filters and form registration.
The table remains one physical table.
Module Syntax
Module: Default
Module: Default LogDataModule
Module: Customer
Module: Customer CustomerDataModule
Rules:
Defaultmeans the module name defaults to the table name.- If the module class name is omitted, the default
DataModuletype is used. - If a module class name is declared, the generated
ModuleDefreferences it.
Form Syntax
Form: DataForm
Form: Customer
Form: Customer CustomerDataForm
Rules:
Form: DataFormmeans default form registration for the module.- If
Formis omitted, the form name defaults to the module name and the form class defaults toDataForm. - To declare a form class name, the form name must also be declared.
ItemPage Syntax
ItemPage: ItemPage
ItemPage: CustomerItemPage
ItemPage declares an item page class name.
If omitted, the default item page class is ItemPage.
DetailOrder Syntax
DetailOrder: Trade=TradeLine, TradeTax
DetailOrder: TradeLine=TradeLineTax
DetailOrder controls the preferred display order of direct child detail tabs.
Rules:
- It belongs to the current module block.
- It may appear multiple times for different parent tables.
- Details not listed remain at the end in declaration order.
Code Syntax
Header-level code syntax:
Code: Code [Draft] [Pattern] [ProviderName]
Code: Draft SO-YYYY-XXXXXX
Code: Draft SO-YYYY-XXXXXX SALES_ORDER
Field-level code syntax:
Code @NVARCHAR(40) @NOT_NULL, -- Code SO-YYYY-XXXXXX SALES_ORDER
Code @NVARCHAR(40) @NOT_NULL, -- Code Draft SO-YYYY-XXXXXX SALES_ORDER
Rules:
- Header
Codebelongs to the current module block. - If header
CodeomitsProviderName, provider name defaults to the module name. - If header
Codeis omitted, field-- Codemetadata is used as fallback. Draftgenerates aDRAFT-provider and the normal provider.- Code provider patterns generate
SYS_NUMBER_SERIESseed statements in generated schema versions.
ListWhere Syntax
ListWhere: DocumentType.ModuleName = 'SalesOrder'
ListWhere: Trade.IsActive = 1
Rules:
- It belongs to the current module block.
- Write only the condition.
- Do not include the
WHEREkeyword. - Only one
ListWhereis allowed per module block. - It may reference the top table and generated join aliases.
The Registration Builder appends it as a where condition after generated joins.
FilterFields Syntax
FilterFields: Code, TradeDate, Person__Name, TradeStatus
Rules:
- It belongs to the current module block.
- Names are resolved against the final generated list select columns.
- Join aliases such as
Person__Namemay be used. - Enum display columns such as
TradeStatusmay be used. - Unknown names are validation errors.
- Duplicate names are validation errors.
- Only one
FilterFieldsdeclaration is allowed per module block. - If omitted, automatic filter generation is used.
FieldGroups Syntax
FieldGroups: Address, Billing, Notes
FieldGroups is table-level metadata.
It defines item page group display order.
The General group is built in and always exists.
Fields without explicit Group metadata are assigned to General.
Field group matching is case-insensitive.
If FieldGroups is present, General is still inserted as the first group even when it is not declared.
Example:
FieldGroups: Address, Billing, Notes
is treated internally as:
General, Address, Billing, Notes
Any group referenced by a field but not listed in FieldGroups is appended after the declared groups in order of first appearance.
If FieldGroups is omitted, groups are ordered by first appearance, with General remaining first.
Fields may declare a group like this:
Street @NVARCHAR(96) @NULL, -- Group Address
Remarks @NBLOB_TEXT @NULL, -- LargeMemo; Group Notes
Header Boolean Flags
Boolean flags are true when present and false when absent.
| Flag | Default |
|---|---|
IsLookup |
heuristic |
NotUiVisible |
false |
IsReadOnly |
false |
IsSingleSelect |
false |
NoFilters |
false |
NoCascadeDeletes |
false |
NoGuidOids |
false |
Example:
IsLookup
NoCascadeDeletes
NoGuidOids
Field Metadata Syntax
Field metadata appears after the first --.
A second -- separates metadata from a plain human comment.
Multiple metadata entries are separated with ;.
FieldName TYPE, -- METADATA -- plain comment
Examples:
CurrencyId @NVARCHAR(40) @NOT_NULL, -- Lookup -- default currency
ModuleName @NVARCHAR(96) @NOT_NULL, -- Lookup DocumentModule ClassName:DocumentModuleLookupSource
Code @NVARCHAR(40) @NOT_NULL, -- Code CUS-XXXX; Group General; [ReadOnlyUI, ReadOnlyEdit] -- customer code
TradeStatusId int default 0 @NOT_NULL, -- Enum TradeStatus; [ReadOnlyUI]
ProductCode @NVARCHAR(40) @NULL, -- Snapshot Product.Code
SupplierCode @NVARCHAR(96) @NULL, -- TitleKey Supplier Product Code
Notes @BLOB_TEXT @NULL, -- Memo; Group Notes -- short notes
Remarks @NBLOB_TEXT @NULL, -- LargeMemo; Group Notes -- long notes
Photo @BLOB @NULL, -- [Image] -- product photo
Field Metadata Keywords
| Keyword | Syntax | Meaning |
|---|---|---|
Master |
Master or Master OneToOne |
Foreign key to parent table. |
Lookup |
Lookup [LOOKUP_NAME] [TableName:TABLE_NAME | EnumName:ENUM_NAME | ClassName:LOOKUP_SOURCE_CLASS_NAME] |
Small reference selector. |
Enum |
Enum [EnumName] |
Enum-backed selector. |
Locator |
Locator [LOCATOR_NAME] [ClassName:LOCATOR_CLASS_NAME] [Form:FORM_NAME] [WebForm:WEB_FORM_NAME] |
Searchable reference selector. |
Code |
Code [Draft] [Pattern] [ProviderName] |
Auto-generated code field. |
Memo |
Memo |
Text field with memo behavior. |
LargeMemo |
LargeMemo |
Text blob with large memo behavior. |
Group |
Group GroupName |
Field UI group. |
Snapshot |
Snapshot TableName.FieldName |
Persisted copy of a related source field. |
TitleKey |
TitleKey KeyOrText |
Field title resource key or fallback text. |
FieldFlags |
[Flag1, Flag2] |
Adds FieldFlags values to the field. |
Notes:
MemoandLargeMemoare mutually exclusive.FieldFlagsnames are parsed from theFieldFlagsenum.- For
FieldFlags, square brackets are part of the actual schema syntax. - In syntax descriptions, square brackets mean optional arguments unless the keyword is
FieldFlags. Snapshotrequires an existing source table and field.TitleKeyuses the remaining text up to the next;.
Common FieldFlags
Common FieldFlags values include:
HiddenReadOnlyReadOnlyUIReadOnlyEditRequiredBooleanMemoLargeMemoImageImagePathNoInsertUpdateForeignKeyExtraSearchable
Default Name Resolution
When names are omitted, the Registration Builder uses conventions.
| Metadata | Default name |
|---|---|
Lookup |
Foreign key referenced table |
Enum |
Field name minus Id suffix |
Locator |
Foreign key referenced table |
field Code provider |
Table name |
header Code provider |
Module name |
Code pattern |
XXXXXX |
Lookup Metadata
Examples:
CustomerId @NVARCHAR(40) @NOT_NULL, -- Lookup
CustomerId @NVARCHAR(40) @NOT_NULL, -- Lookup Customer
PersonId @NVARCHAR(40) @NOT_NULL, -- Lookup Customer TableName:Person
TradeTypeId int @NOT_NULL, -- Lookup TradeType EnumName:TradeType
ModuleName @NVARCHAR(96) @NOT_NULL, -- Lookup DocumentModule ClassName:DocumentModuleLookupSource
Rules:
- If
LOOKUP_NAMEis omitted, it is resolved from the foreign key referenced table or from the field name withoutId. - If
TableName:,EnumName:orClassName:is used,LOOKUP_NAMEis required. TableName:generates table-backed lookup registration.EnumName:generates enum lookup source registration.ClassName:generates class-backed lookup source registration.
Locator Metadata
Examples:
ProductId @NVARCHAR(40) @NOT_NULL, -- Locator
ProductId @NVARCHAR(40) @NOT_NULL, -- Locator Product
ProductId @NVARCHAR(40) @NOT_NULL, -- Locator Product ClassName:ProductLocator
CustomerId @NVARCHAR(40) @NOT_NULL, -- Locator Customer Form:Person WebForm:Person
PaymentId @NVARCHAR(40) @NOT_NULL, -- Locator Payment ClassName:PaymentLocator Form:CustomerReceipt WebForm:CustomerReceipt
Rules:
- If
LOCATOR_NAMEis omitted, it is resolved from the foreign key referenced table. - If
ClassName:,Form:, orWebForm:is used,LOCATOR_NAMEis required. - The builder generates
LocatorDefregistration. - The generated table field references the locator name.
Form:is the desktop reference form name used by locator reference menus.WebForm:is the web reference form name used by web locator reference menus.- For FK-backed generated locators, if
Form:is omitted, the builder uses the referenced table form. - For FK-backed generated locators, if
WebForm:is omitted, the builder uses the referenced table web form, falling back toForm:. - At runtime,
LocatorDef.Formfalls back to the locator name andLocatorDef.WebFormfalls back toForm. - For table-backed locators where the locator name is the referenced module/form name, the defaults are usually enough.
- For custom SQL, service, or cross-module locators, prefer explicit
Form:andWebForm:.
Join aliases use this convention:
JOIN_ALIAS__FIELD_NAME
Example:
Product__Code
Product__Name
Snapshot fields may receive locator values when they point to the same joined source field.
ProductCode @NVARCHAR(40) @NULL, -- Snapshot Product.Code
ProductName @NVARCHAR(96) @NULL, -- Snapshot Product.Name
The preferred locator field names are the source field names, such as Code and Name.
When a custom locator SELECT returns different column names, keep the source name and set an explicit alias in code.
LocatorDef.Add("Code", DataFieldType.String, TargetField: null, Alias: "ProductCode", TitleKey: null, IsVisible: true, IsSearchable: true);
LocatorDef.Add("Name", DataFieldType.String, TargetField: null, Alias: "ProductName", TitleKey: null, IsVisible: true, IsSearchable: true);
Then the SELECT may return the aliased columns:
select
P.Id as Id
,P.Code as ProductCode
,P.Name as ProductName
from Product P
Table Lookup Detection
A table is identified as a lookup when:
- Its header contains
IsLookup, or - it is a top table whose native fields match one of the known lookup shapes.
Common lookup shapes:
Id, Name
Id, Code, Name
Id, Name, IsActive
Id, Code, Name, IsActive
Explicit IsLookup overrides the heuristic.
Practical Rule
When writing metadata comments:
- Put module metadata only on top tables.
- Keep module blocks ordered and easy to scan.
- Use
ListWherewithout theWHEREkeyword. - Use
FilterFieldsnames from the final generated list select. - Keep field metadata close to the field it describes.
- Separate multiple field metadata entries with
;. - Use a second
--for plain human comments. - Use
Lookupfor small selectors. - Use
Locatorfor searchable references. - Use
Snapshotfor persisted copies of related fields. - Use header
Codefor multi-module document tables. - Do not put business behavior in metadata comments.