Skip to Content

Database Action

The db action executes SQL queries on MySQL, PostgreSQL, and SQLite databases, providing comprehensive result handling and error reporting.

Basic Syntax

steps: - name: "Database Query" uses: db with: dsn: "mysql://user:password@localhost:3306/database" query: "SELECT * FROM users WHERE active = ?" params: [true] test: res.code == 0 && res.rows_affected > 0

Parameters

dsn (required)

Type: String
Description: Database connection string with automatic driver detection
Supports: Template expressions

# MySQL vars: db_pass: "{{DB_PASS}}" with: dsn: "mysql://user:password@localhost:3306/database" dsn: "mysql://{{vars.db_user}}:{{vars.db_pass}}@{{vars.db_host}}/{{vars.db_name}}" # PostgreSQL vars: pg_user: "{{PG_USER}}" pg_pass: "{{PG_PASS}}" pg_host: "{{PG_HOST}}" pg_db: "{{PG_DB}}" with: dsn: "postgres://user:password@localhost:5432/database?sslmode=disable" dsn: "postgres://{{vars.pg_user}}:{{vars.pg_pass}}@{{vars.pg_host}}/{{vars.pg_db}}" # SQLite with: dsn: "file:./testdata/sqlite.db" dsn: "file:/absolute/path/to/database.db" dsn: "file:{{vars.data_dir}}/app.db"

query (required)

Type: String
Description: SQL query to execute
Supports: Template expressions and multi-line strings

with: query: "SELECT * FROM users" query: "INSERT INTO logs (message, timestamp) VALUES (?, NOW())" query: | SELECT u.name, u.email, p.title FROM users u JOIN profiles p ON u.id = p.user_id WHERE u.active = ? AND u.created_at > ?

params (optional)

Type: Array of mixed values (String, Number, Boolean)
Description: Query parameters for prepared statements
Supports: Template expressions

with: query: "SELECT * FROM users WHERE id = ? AND active = ?" params: [123, true, "{{vars.user_email}}"]

timeout (optional)

Type: Duration
Default: 30s
Description: Query execution timeout

with: query: "SELECT COUNT(*) FROM large_table" timeout: "60s"

Response Object

The database action provides a res object with the following properties:

PropertyTypeDescription
codeIntegerOperation result (0 = success, 1 = error)
rows_affectedIntegerNumber of rows affected by the query
rowsArrayQuery results for SELECT statements (as objects)
errorStringError message if operation failed

Response Examples

SELECT Query Response

steps: - name: "Fetch Users" id: fetch-users uses: db with: dsn: "mysql://user:pass@localhost/db" query: "SELECT id, name, email FROM users WHERE active = ?" params: [true] test: res.code == 0 && res.rows_affected > 0 outputs: user_count: res.rows_affected first_user_id: res.rows__0__id first_user_name: res.rows__0__name

INSERT/UPDATE Query Response

steps: - name: "Insert User" uses: db with: dsn: "postgres://user:pass@localhost/db" query: "INSERT INTO users (name, email) VALUES ($1, $2)" params: ["John Doe", "john@example.com"] test: res.code == 0 && res.rows_affected == 1

Database-Specific Features

MySQL Examples

# MySQL with connection options - name: "MySQL Query" uses: db with: dsn: "mysql://user:pass@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=true" query: "SELECT VERSION() as mysql_version, NOW() as current_time" test: res.code == 0 # MySQL stored procedure - name: "Call Procedure" uses: db with: dsn: "mysql://user:pass@localhost:3306/database" query: "CALL GetUsersByDepartment(?)" params: ["Engineering"] test: res.code == 0

PostgreSQL Examples

# PostgreSQL with JSON operations - name: "JSON Query" uses: db with: dsn: "postgres://user:pass@localhost:5432/database?sslmode=disable" query: | SELECT name, data->>'role' as role, data->'preferences' as prefs FROM users WHERE data ? 'role' AND data->>'role' = $1 params: ["admin"] test: res.code == 0 # PostgreSQL array operations - name: "Array Query" uses: db with: dsn: "postgres://user:pass@localhost:5432/database" query: "SELECT name FROM users WHERE tags && $1" params: ['{"admin","moderator"}'] test: res.code == 0

SQLite Examples

# SQLite with file creation - name: "SQLite Query" uses: db with: dsn: "file:./testdata/sqlite.db" query: | CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) test: res.code == 0 # SQLite with in-memory database - name: "Memory Database" uses: db with: dsn: "file::memory:" query: "CREATE TABLE temp_data (id INTEGER, value TEXT)" test: res.code == 0

Common Query Patterns

Data Validation Queries

- name: "Check Data Integrity" uses: db with: dsn: "mysql://user:pass@localhost/db" query: | SELECT COUNT(*) as total_users, COUNT(CASE WHEN active = 1 THEN 1 END) as active_users, COUNT(CASE WHEN email IS NULL THEN 1 END) as missing_emails FROM users test: | res.code == 0 && res.rows__0__total_users > 0 && res.rows__0__missing_emails == 0

Performance Monitoring

- name: "Database Performance Check" uses: db with: dsn: "postgres://user:pass@localhost/db" query: | SELECT schemaname, tablename, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch FROM pg_stat_user_tables WHERE seq_scan > 1000 timeout: "10s" test: res.code == 0 outputs: high_seq_scan_tables: res.rows_affected

Batch Operations

- name: "Batch Insert" uses: db with: dsn: "mysql://user:pass@localhost/db" query: | INSERT INTO audit_log (action, table_name, record_id, timestamp) VALUES ('CREATE', 'users', 123, NOW()), ('UPDATE', 'profiles', 456, NOW()), ('DELETE', 'sessions', 789, NOW()) test: res.code == 0 && res.rows_affected == 3

Security Features

The database action implements several security measures:

  • Prepared Statements: All parameterized queries use prepared statements to prevent SQL injection
  • Connection String Masking: Passwords are masked in logs and output
  • Timeout Protection: Prevents long-running queries from hanging
  • Driver Validation: Only supports approved database drivers
  • DSN Validation: Validates connection string format before execution

Error Handling

Common error scenarios and handling patterns:

- name: "Database with Error Handling" uses: db with: dsn: "mysql://user:pass@localhost/db" query: "SELECT * FROM users WHERE id = ?" params: [999999] test: | res.code == 0 ? true : res.error | contains("connection") ? false : res.error | contains("not found") ? true : false outputs: query_success: res.code == 0 error_type: | {{res.code == 0 ? "none" : res.error | contains("connection") ? "connection" : res.error | contains("syntax") ? "syntax" : "unknown"}}

Transaction Examples

While the action doesn’t directly support transactions, you can use database-specific transaction syntax:

# PostgreSQL transaction - name: "Begin Transaction" uses: db with: dsn: "postgres://user:pass@localhost/db" query: "BEGIN" test: res.code == 0 - name: "Insert Data" uses: db with: dsn: "postgres://user:pass@localhost/db" query: "INSERT INTO users (name) VALUES ($1)" params: ["Test User"] test: res.code == 0 - name: "Commit Transaction" uses: db with: dsn: "postgres://user:pass@localhost/db" query: "COMMIT" test: res.code == 0
Updated at