Database Query
Run a SQL statement against one of your databases and capture the result.
What it does
Executes one SQL statement — reading or writing — and writes the resulting rows to memory. A later step can iterate over the rows, send them to an AI for analysis, write them to a file, or react conditionally.
The connection is configured in the step itself. Knowledge bases of type Database (live) are a separate thing: they give agents and AI models read access as an information source and are not used by workflow steps.
Values from the run are inserted with {memory://key} placeholders. They are passed to the database as parameters, never pasted into the SQL text — a value like x'); DROP TABLE orders; -- is stored as exactly that text and cannot change the statement.
What you configure
| Field | What it controls | Required | Notes |
|---|---|---|---|
| Database type | postgres, mysql (also MariaDB), mssql (SQL Server) or sqlite. | required | |
| Server / Port | Where the database runs. | required except SQLite | Default ports 5432 / 3306 / 1433. |
| User / Password | Login for the database. | as the database needs | The password is stored encrypted. |
| Database | Database name. | required | SQLite: absolute file path on the server, or a libSQL address. |
| Encrypted connection (TLS) | Connect via TLS. | optional | Off by default, on for SQL Server. |
| SQL statement | The statement to execute. | required | One statement. Values from memory as {memory://key} or {memory://key/field}. |
| Max. rows | Upper limit for returned rows. | optional | Default 10 000; larger results are cut off with a warning in the run log. |
| Result (rows) | Where the rows are stored. | optional | A list; each row is an object with the column names as keys. Empty for statements that return no rows. |
| Affected rows | Number of rows written by INSERT, UPDATE or DELETE. | optional | |
| Error message | Where to store the error if the statement fails. | optional | If set, a failing statement does not stop the run — the message lands here and the next steps continue. |
Placeholders in the statement
| Written as | Becomes | Example |
|---|---|---|
{memory://key} | a parameter | WHERE order_no = {memory://order_no} |
'{memory://key}' (the whole literal) | a parameter — the quotes are dropped | INSERT INTO notes VALUES ('{memory://text}') |
inside a longer text, e.g. '%{memory://q}%' | rejected | build the text in SQL: LIKE '%' || {memory://q} || '%' |
| as a table or column name | rejected | write names directly into the statement |
Lists and objects are passed as JSON text; a missing value is passed as NULL.
Writing to the database
The step writes with the rights of the database user you enter. Give that user only the rights the workflow needs — for example INSERT on one table — and use a separate, read-only user for steps that only read.
Example scenario
Customer lookup. A webhook trigger receives an order number. A Database Query step runs SELECT c.name, c.email FROM orders o JOIN customers c ON c.id = o.customer_id WHERE o.no = {memory://order_no}. The result is fed to an AI action that drafts a personalized email, which is sent in the final step.
Recommendations
- ✅ Use a dedicated read-only user for steps that only read.
- ✅ Limit result size with
LIMIT(orTOP). Max. rows cuts off larger results; the run log then shows a warning. - ✅ Use Error message plus a condition on the next step if the workflow should handle a failing query itself.
- ⚠️ Time-zone handling depends on the database. If you compare dates, store them in UTC.
- ❌ Do not run DDL statements (
DROP,ALTER, …) from this step in production workflows. Reserve schema changes for proper migration tooling.
What to do next
- For runs started by new or changed rows: Database Listener.
- To let agents look things up in a database: a knowledge base of type Database (live).
- For sending data to external HTTP APIs: HTTP Request.