@@ -22,6 +22,7 @@ Field names are **exact** - use them precisely in your workflows.
2222| ReadJSONState | path |
2323| WriteJSONState | path, data |
2424| MergeJSONState | path, updates |
25+ | Sql | engine |
2526
2627### Common Field Name Mistakes
2728
@@ -484,6 +485,145 @@ Without this, execution fails with configuration error.
484485
485486---
486487
488+ # # Sql
489+
490+ **Description**: SQL executor for database operations.
491+
492+ # ## Required Inputs
493+
494+ - **`engine`** (string): Database engine. Required.
495+
496+ # ## Optional Inputs
497+
498+ - **`path`** (any): SQLite: Database file path. Use ':memory:' for in-memory DB.
499+ - **`host`** (any): Database host
500+ - **`port`** (any): Database port (default: 5432 for PostgreSQL, 3306 for MariaDB)
501+ - **`database`** (any): Database name
502+ - **`username`** (any): Database username
503+ - **`password`** (any): Database password. Use {{secrets.DB_PASSWORD}} for security.
504+ - **`sql`** (any):
505+ SQL statement(s) to execute (Raw SQL mode).
506+ - Use ? for positional params (SQLite) or $1, $2 for PostgreSQL
507+ - MariaDB uses %s for positional params
508+ - Multi-statement scripts : separate with semicolons
509+ Mutually exclusive with 'model' field.
510+
511+ - **`params`** (any):
512+ Query parameters for raw SQL (prevents SQL injection).
513+ - List for positional : [value1, value2]
514+ - Dict for named : {"name": value} (PostgreSQL/MariaDB)
515+
516+ - **`model`** (any):
517+ Model schema for CRUD operations (Model mode).
518+ Defines table structure with columns, types, indexes.
519+ Mutually exclusive with 'sql' field.
520+ Example :
521+ model :
522+ table : tasks
523+ columns :
524+ id : {type: text, primary: true, auto: uuid}
525+ name : {type: text, required: true}
526+ indexes :
527+ - columns : [name]
528+
529+ - **`op`** (any):
530+ CRUD operation (required when using model mode).
531+ - schema : Create table + indexes
532+ - insert : Insert row (requires data)
533+ - select : Query rows (optional where, order, limit, offset)
534+ - update : Update rows (requires where and data)
535+ - delete : Delete rows (requires where)
536+ - upsert : Insert or update on conflict (requires data and conflict)
537+
538+ - **`data`** (any): Row data for insert/update/upsert operations.
539+ - **`where`** (any):
540+ Filter conditions for select/update/delete.
541+ - Simple equality : {status: running}
542+ - Operators : {priority: {">": 5}}
543+ - IN : {type: {in: [a, b, c]}}
544+ - IS NULL : {deleted_at: {is: null}}
545+
546+ - **`order`** (any): Sort order for select. Format: ["column:asc", "column:desc"]
547+ - **`limit`** (any): Maximum rows to return (select).
548+ - **`offset`** (any): Rows to skip (select).
549+ - **`conflict`** (any): Conflict columns for upsert (usually primary key).
550+ - **`init_sql`** (any):
551+ DDL to execute before the main operation (idempotent).
552+ Use CREATE TABLE IF NOT EXISTS, CREATE INDEX IF NOT EXISTS, etc.
553+
554+ - **`isolation_level`** (any):
555+ Transaction isolation level.
556+ - PostgreSQL/MariaDB : read_uncommitted, read_committed, repeatable_read, serializable
557+ - SQLite : immediate (recommended for writes), exclusive, or default (deferred)
558+
559+ - **`ssl`** (any) *(default: `False`)*: Enable SSL/TLS. Boolean or sslmode string (require, verify-ca, verify-full)
560+ - **`timeout`** (any) *(default: `30`)*: Query execution timeout in seconds
561+ - **`connect_timeout`** (any) *(default: `10`)*: Connection establishment timeout in seconds
562+ - **`pool_size`** (any) *(default: `5`)*: Connection pool size (PostgreSQL/MariaDB only)
563+ - **`sqlite_pragmas`** (any):
564+ SQLite PRAGMA settings applied on connection.
565+ Defaults : journal_mode=WAL, busy_timeout=30000, synchronous=NORMAL, foreign_keys=ON
566+
567+
568+ # ## Outputs
569+
570+ - **`meta`** (object): Executor-specific metadata fields (exit_code, tokens_used, etc.)
571+ - **`rows`** (array): Result rows as list of dicts
572+ - **`columns`** (array): Column names from result set
573+ - **`row_count`** (integer): Number of rows returned (select) or affected (insert/update/delete)
574+ - **`affected_rows`** (integer): Rows affected by INSERT/UPDATE/DELETE
575+ - **`last_insert_id`** (any): Last inserted row ID (auto-increment)
576+ - **`success`** (boolean): Operation completed successfully
577+ - **`engine`** (string): Database engine used
578+ - **`execution_time_ms`** (number): Query execution time in milliseconds
579+
580+ # ## Example
581+
582+ ` ` ` yaml
583+ # Raw SQL mode - SQLite query
584+ - id: get_users
585+ type: Sql
586+ inputs:
587+ engine: sqlite
588+ path: "/data/app.db"
589+ sql: "SELECT * FROM users WHERE status = ?"
590+ params: ["active"]
591+
592+ # Model mode - Create table and insert
593+ - id: create_task
594+ type: Sql
595+ inputs:
596+ engine: sqlite
597+ path: "{{state.db_path}}"
598+ model:
599+ table: tasks
600+ columns:
601+ task_id: {type: text, primary: true, auto: uuid}
602+ name: {type: text, required: true}
603+ status: {type: text, default: pending}
604+ created_at: {type: timestamp, auto: created}
605+ indexes:
606+ - columns: [status]
607+ op: insert
608+ data:
609+ name: "My Task"
610+
611+ # Model mode - Select with filters
612+ - id: find_tasks
613+ type: Sql
614+ inputs:
615+ engine: sqlite
616+ path: "{{state.db_path}}"
617+ model: "{{inputs.models.task}}"
618+ op: select
619+ where:
620+ status: running
621+ order: [created_at:desc]
622+ limit: 10
623+ ` ` `
624+
625+ ---
626+
487627# # Variable Access Patterns
488628
489629Use double braces `{{ }}` for variable interpolation in YAML :
0 commit comments