Data
In the admin: Workflows › a Workflow › a step
The four Data steps read and write the rows of your tables through the same engine as the Data API. They act as the run's executor: its permissions, row filters, field grants and payload checks apply, and PostgreSQL's constraints and triggers decide as for any other write. A step never writes anything the executor couldn't write through the API.
| Step | Does | Result, as {{step.data}} |
|---|---|---|
| Read records | Query a table or view | the rows read |
| Create records | Insert one or more records | the rows created |
| Update records | Modify the rows a filter selects | the rows updated |
| Delete records | Delete the rows a filter selects | the rows deleted |
Each step names its table by its identity, not its name: renaming the table in PostgreSQL keeps the step working, while dropping it and creating another with the same name doesn't silently retarget it. Publishing refuses a step whose table no longer exists.
Read records
| Field | |
|---|---|
| Resource | Required. An ACTIVE table or view the executor may read |
| Query | Fields, filter, search, grouping, ordering, limit and offset: the Data API's query, built with Build or written as JSON |
The executor needs READ. Their row filter and field grants apply; password columns are never returned. The Data API's limits apply too: without a limit, the default page size (api.default_limit) is read, never more than api.max_limit. Read nothing more than a step needs.
An empty result is a success: check it with a Condition, such as {"length": {"_eq": "0"}} on {{read_orders.data}}.
Acme's Service request intake finds the site of the customer who wrote in:
{
"fields": "id,branch,customer",
"where": { "customer.email": { "_eq": "{{start.data.body.email}}" } },
"limit": 1
}
The next step reads the site as {{find_site.data[0].id}}.
{"quantity": {"_gt": 0}}. A {{…}} used as a filter value may resolve to any of them. _in, _nin and _between take a list, ["COMPLETED", "CANCELLED"], or comma-separated text, "COMPLETED,CANCELLED" - so a DataView selection's ids list goes into _in as it is.Create records
| Field | |
|---|---|
| Resource | Required. An ACTIVE table the executor may create rows in |
| Records | A JSON array, one object per record. Values can be {{…}} |
| Emit database events | Off by default. See below |
[
{
"site": "{{find_site.data[0].id}}",
"branch": "{{find_site.data[0].branch}}",
"type": "REPAIR",
"priority": "NORMAL",
"status": "NEW",
"description": "{{start.data.body.description}}"
}
]
All the records are created in one transaction: if PostgreSQL refuses one, such as a failed CHECK or a missing required column, none is created and the step fails with PostgreSQL's message. Columns PostgreSQL fills, such as id, number or due_at at Acme, come back in the result: {{create_order.data[0].number}}.
Records may also be a single {{…}} that resolves to a list, such as the rows a Parse CSV step produced.
Update records
| Field | |
|---|---|
| Resource | Required. An ACTIVE table the executor may update |
| Filter (where) | Which rows to update, in the filter grammar, built in Simple or written in Advanced |
| Set | A JSON object: the columns to change and their new values |
| Emit database events | Off by default |
The update is checked like any Data API update: the executor's row filter must allow each row before and after the change, and their payload checks must hold. All rows change in one transaction, or none does. No row matching is a success, with an empty result.
A step with no filter would update every row the executor may update. While Require a filter for bulk update (api.require_where_for_update) is on, the default, such a step can't be published, and fails if the setting is turned on later.
Delete records
| Field | |
|---|---|
| Resource | Required. An ACTIVE table the executor may delete from |
| Filter (where) | Which rows to delete. Required while Require a filter for bulk delete (api.require_where_for_delete) is on |
| Emit database events | Off by default |
All rows are deleted in one transaction. The result is the deleted rows, as they were.
Audit and events
Every write is in the audit log like any other: the executor as who did it, workflow as where it came from, and the run and step it belongs to. Audit entries in a run's detail opens them.
Emit database events
A write by a workflow starts Database event workflows only when its step's Emit database events is on. Leave it off unless another workflow should react to this write. When it's on, TotalBase still refuses a workflow that would start itself again, directly or through others, and chains longer than workflows.max_chain_depth workflows (8 by default).
Execute SQL and Execute function never produce database events.
Errors
| Code | When |
|---|---|
ACTION_DENIED | the executor lacks the permission the step needs |
RESOURCE_NOT_FOUND | the table is gone, not ACTIVE, or of the wrong kind |
INVALID_QUERY | an invalid filter or query, or a missing filter the Data API requires |
| PostgreSQL's | a constraint, a trigger's RAISE EXCEPTION, mapped as in the Data API |
Connect the step's Error output to handle them; see Errors.