Fields are where you can spell out what each table column means by using certain clauses, such as its type (TYPE), optional default values (DEFAULT), how writes are normalised (VALUE), what counts as valid data (ASSERT), or who may see or change it (PERMISSIONS).
You need database-level access and an active USE scope, same as for tables.
Computed fields (derived on every read) have their own learn page: Computed fields.
Example usage
The following expression shows the simplest way to use the DEFINE FIELD statement.
The fields of an object and the items in an array can be defined individually using the . operator for objects, or the indexing operator for arrays.
Simple data types
A | vertical bar can be used to allow a field to be one of a set of types. The following example shows a field that can be a UUID or an int, perhaps for user records that have varying data due to two diffent legacy ID types.
Array type
You can also set a field to have the array data type. The array data type can be used to store a list of values. You can also set the data type of the array's contents, as well as the required number of items that it must hold.
Making a field optional
You can make a field optional by wrapping the inner type in an option, which allows you to store NONE values in the field.
The example below shows how to define a field user on a POST table. The field is of type record. This means that the field can store a record<user> or NONE.
Flexible objects in schemafull tables
The FLEXIBLE keyword allows you to have SCHEMALESS functionality for an object on a SCHEMAFULL table.
Taking the following CREATE statement:
Without FLEXIBLE, the metadata field will effectively be a SCHEMAFULL object with only a single defined field.
With FLEXIBLE, the output will be as expected as the schema now allows any sort of object to be a field on the user table, as long as values for name and metadata.user_id are present.
Using the DEFAULT clause to set a default value
You can set a default value for a field using the DEFAULT clause. The default value will be used if no value is provided for the field.
Using the DEFAULT ALWAYS clause
In addition to the DEFAULT clause, you can use the DEFAULT ALWAYS clause to set a default value for a field. The ALWAYS keyword indicates that the DEFAULT clause is used not only on CREATE, but also on UPDATE if the value is empty (NONE).
With the above definition, the primary field will be set to 123.456 when a new product is created without a value for the primary field or with a value of NONE, and when an existing product is updated if the value is specified the result will be the new value.
In the case of NULL or a mismatching type, an error will be returned.
On the other hand, if a valid number is provided during creation or update, that number will be used instead of the default value. In this case, 123.456.
Using the VALUE clause to set a field's value
The VALUE clause differs from DEFAULT in that a default value is calculated if no other is indicated, otherwise accepting the value given in a query.
A VALUE clause, on the other hand, will ignore attempts to set the field to any other value.
As the example above shows, a VALUE clause sets the value every time a record is modified (created or updated). However, the value will not be recalculated in a SELECT statement, which simply accesses the current set value.
To create a field that is calculated each time it is accessed, a computed field can be used.
Altering a passed value
You can alter a passed value using the VALUE clause. This is useful for altering the value of a field before it is stored in the database.
In the example below, the VALUE clause is used to ensure that the email address is always stored in lowercase characters by using the string::lowercase function.
Asserting rules on fields
You can take your field definitions even further by using asserts. Assert can be used to ensure that your data remains consistent. For example you can use asserts to ensure that a field is always a valid email address, or that a number is always positive.
As the ASSERT clause expects an expression that returns a boolean, an assertion with a custom message can be manually created by returning true in one case and using a THROW clause otherwise.
Making a field READONLY
The READONLY clause can be used to prevent any updates to a field. This is useful for fields that are automatically updated by the system. To make a field READONLY, add the READONLY clause to the DEFINE FIELD statement. As seen in the example below, the created field is set to READONLY.
Setting permissions on fields
By default, the permissions on a field will be set to FULL unless otherwise specified.
You can set permissions on fields to control who can perform operations on them using the PERMISSIONS clause. The PERMISSIONS clause can be used to set permissions for SELECT, CREATE, and UPDATE operations. The DELETE operation only relates to records and, as such, is not available for fields.
Order of operations when setting a field's value
As DEFINE FIELD statements are computed in alphabetical order, be sure to keep this in mind when using fields that rely on the values of others.
The following example is identical to the above except that full_name has been chosen for the previous field name. The full_name field will be calculated after first_name, but before last_name.
A good rule of thumb is to organize your DEFINE FIELD statements in alphabetical order so that the field definitions show up in the same order as that in which they are computed.
Defining a literal on a field
A field can also be defined as a literal type, by specifying one or more possible values and/or permitted types.