sql://¶
Driver-agnostic SQL producer + polling consumer. One component instance
handles every driver the running Helix binary blank-imports — MySQL,
MariaDB, Microsoft SQL Server, Oracle, PostgreSQL (via pgx/stdlib),
SQLite, and anything else registered via database/sql.Register.
URI shapes¶
The component supports two URI forms:
1. CRUD shape (existing Helix convention):
operation ∈ {query, insert, update, delete, execute}.
2. Raw-SQL shape:
The query can also be supplied per-request via the
HelixSqlQuery header, or via the message body when
useMessageBodyForSql=true.
Parameters¶
Common / raw-SQL form¶
| Param | Type | Default | Description |
|---|---|---|---|
driver |
string | required | database/sql.Register name (mysql, pgx, sqlserver, oracle, sqlite3, …). |
dsn |
string | required | Driver-specific connection string. |
query |
string | — | Raw SQL query. Per-request override via header. |
allowNamedParameters |
boolean | true |
Parse :#name placeholders into driver-appropriate positional placeholders. |
placeholder |
string | # |
Named-parameter prefix character (the :#name form uses #). |
usePlaceholder |
boolean | true |
When false, the SQL is sent as-is — no rewriting. |
outputType |
enum | SelectList |
Result shape — SelectList, SelectOne, Update, StreamList. |
outputHeader |
string | — | Write the result to this header instead of the body. |
separator |
string | , |
Split character for IN-clause CSV expansion (SELECT … WHERE id IN (:#ids) with a string body of "1,2,3" expands to three placeholders). |
Producer¶
| Param | Type | Default | Description |
|---|---|---|---|
batch |
boolean | false |
Treat the body as []map[string]any and run the SQL once per row in a single statement loop. |
batchAutoCommitDisabled |
boolean | true |
Defer commit until end-of-batch. Rolls back the whole batch on first failure. |
noop |
boolean | false |
Skip executing the SQL — just bind parameters and log. Dry-run validation. |
useMessageBodyForSql |
boolean | false |
Body IS the SQL query string; params come from HelixSqlParameters header. |
useHeadersAsParameters |
boolean | false |
Source named-param values from exchange headers (vs. body map). Body is fallback. |
transacted |
boolean | false |
Wrap the producer call (or consumer batch + onConsume hooks) in a single DB transaction. |
alwaysPopulateStatement |
boolean | true |
When true, missing :#name bind values cause an error. When false, missing names bind as SQL NULL (lax mode). |
readSize |
integer | 0 |
Cap on rows returned per query. 0 = no cap. |
Consumer (polling)¶
| Param | Type | Default | Description |
|---|---|---|---|
delay |
duration | 500ms |
Poll interval. Accepts Go duration (5s) or bare ms (500). |
initialDelay |
duration | 1s |
Delay before the first poll. |
useFixedDelay |
boolean | true |
Fixed-delay scheduling vs. fixed-rate. |
maxMessagesPerPoll |
integer | 0 |
Cap rows processed per poll cycle. 0 = unlimited. |
repeatCount |
integer | 0 |
Number of polls before the consumer stops. 0 = infinite. |
useIterator |
boolean | true |
Emit one Exchange per row vs. one Exchange per poll batch. |
routeEmptyResultSet |
boolean | false |
Emit an empty-body Exchange when a poll yields zero rows. |
sendEmptyMessageWhenIdle |
boolean | false |
Emit a heartbeat Exchange (with sql.empty=true) when a poll yields zero rows. |
breakBatchOnConsumeFail |
boolean | false |
Abort the batch on first failing row. |
expectedUpdateCount |
integer | -1 |
Fail the batch if an onConsume statement affects ≠ N rows. -1 disables. |
greedy |
boolean | false |
Re-poll immediately when previous poll yielded rows. |
onConsume |
string | — | SQL run on each row after the processor succeeds. Typically UPDATE … SET done=true WHERE id=:#id. |
onConsumeFailed |
string | — | SQL run on each row after the processor fails. |
onConsumeBatchComplete |
string | — | SQL run once after the whole poll batch completes. |
Legacy / Helix-only¶
| Param | Type | Default | Description |
|---|---|---|---|
pollInterval |
duration | — | Legacy alias for delay. Existing routes use this name. |
pollSince |
string | — | Column used as a watermark for change-data-capture polling. The consumer establishes max(col) on first poll, then emits only rows newer than the watermark. |
Header conventions¶
Each header is also accepted under its legacy alternate name. Read priority is the Helix form, then the alternate; writes set both names.
| Header | Purpose |
|---|---|
HelixSqlQuery |
Per-request query override (unless skipControlHeaders). |
HelixSqlParameters |
Named-param map (used with useMessageBodyForSql). |
HelixSqlDataSource |
Per-request DSN override. |
HelixSqlRowCount |
Written on successful SELECT. |
HelixSqlUpdateCount |
Written on UPDATE/DELETE/INSERT. |
HelixSqlRetrieveGeneratedKeys |
Opt into RETURNING / generated keys. |
HelixSqlGeneratedColumns |
Column list for RETURNING (pg). |
HelixSqlGeneratedKeyRows |
[]map[string]any of inserted rows. |
sql.where |
Legacy — required by update and delete CRUD ops. |
sql.rowCount / sql.rowsAffected |
Legacy aliases (also written). |
Body shape¶
Request:
- CRUD
insert/update:map[string]any(column → value). - CRUD
query: optional{query, params}map override. - CRUD
execute:{query, params}map. - CRUD
batch=trueinsert:[]map[string]any. - Raw-SQL:
map[string]anyfor named params (unlessuseHeadersAsParameters);[]anyfor positional. useMessageBodyForSql=true: body IS the SQL string.
Response (in priority order):
outputHeaderset → result on that header, body untouched.outputType=SelectList(default):[]map[string]any.outputType=SelectOne:map[string]any(errors if ≠ 1 row).outputType=Update:int64rows-affected.outputType=StreamList:<-chan map[string]anyfor streaming large result sets.
Examples¶
CRUD insert¶
Raw SELECT with named params¶
- to: sql://?driver=pgx&dsn=postgres://app:secret@db:5432/app
# body: {"id": 42} → :#id resolves to 42, rewritten to $1
Body:
Query (URL-encoded inline or supplied via HelixSqlQuery header):
Batch insert¶
- to: sql://events/insert?driver=mysql&dsn=…&batch=true
# body: [{"id": 1, "t": "..."}, {"id": 2, "t": "..."}, …]
Polling consumer with onConsume hook¶
trigger:
uri: sql://jobs/query?driver=pgx&dsn=…&delay=500ms&onConsume=UPDATE%20jobs%20SET%20done%3Dtrue%20WHERE%20id%3D%3A%23id
query: "SELECT id, payload FROM jobs WHERE done = false"
steps:
- process:
ref: handle-job
outputType=Update with named param¶
- to: sql://?driver=mysql&dsn=…&outputType=Update
# body: {"delta": 5}
# query (header HelixSqlQuery): UPDATE counters SET v = v + :#delta
See also¶
postgres://— pgx-native sibling with LISTEN/NOTIFY consumer.