Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra
Command line & CI

Command line & CI

elyrasql-cli is the client without its window. It does three jobs, for CI or any script:

  • check runs your data-quality checks: queries that return the rows breaking a rule, and pass when they return none.
  • preflight reads migration files and asks the database what each statement would do, without running it. It reports how many rows an UPDATE or DELETE touches, and whether an ALTER that adds a rule would fail on the rows already there, or (on ElyraSQL) succeed and leave them breaking it.
  • run runs runbooks: it prints each block's result and judges the blocks that say what they should return.

All three only read. The connection is opened read-only, so the server refuses any write, whatever the files hold.

Requires Pro for remote servers

As in the app, a remote connection needs Pro. A local server (127.0.0.1, localhost) and SQLite files are free. In CI, put your license key in the ELYRASQL_LICENSE_KEY secret. It is checked online for that run only and stored nowhere, and every run with the same key counts as one device.

Install

Each release publishes elyrasql-cli beside the app, at https://elyracode.com/downloads/sql-client/:

File For
elyrasql-cli-VERSION-linux-x86_64.tar.gz Linux x86_64, such as GitHub's ubuntu-latest
elyrasql-cli-VERSION-linux-aarch64.tar.gz Linux on ARM
elyrasql-cli-VERSION-macos-arm64.tar.gz macOS on Apple silicon

Each has a .sha256 file beside it. The macOS binary is signed and notarized by Apple, so Gatekeeper lets it run when it was downloaded in a browser. On Linux, OpenSSL and D-Bus are built in and it needs only glibc 2.34 or later, so it runs on a bare CI image: ubuntu-latest, ubuntu:22.04, debian:bookworm-slim. To build it yourself instead:

cargo build --release --bin elyrasql-cli --no-default-features --features vendored

Connecting

Give a URL with --url, or set ELYRASQL_URL. Put the password in ELYRASQL_PASSWORD rather than in the URL.

Database URL
MySQL mysql://ci@db.staging:3306/shop
MariaDB mariadb://ci@db.staging:3306/shop
ElyraSQL elyrasql://ci@db.staging:3307/elyra
ClickHouse clickhouse://default@ch.staging:8123/analytics (checks only)
SQLite sqlite:///path/to/file.db or sqlite:relative.db

Add ?tls=true to require TLS, and --timeout 60 to change the limit per statement (default 30 s).

On your own machine, --connection staging uses a connection saved in ElyraSQL Client, with its password from the keychain. elyrasql-cli connections lists them.

Checks

Keep checks as .sql files in the repository. Every statement is a check. Name each one with a -- @name: line above it:

-- @name: Every order has a customer
SELECT o.id FROM orders o
LEFT JOIN customers c ON c.id = o.customer_id
WHERE c.id IS NULL;

-- @name: No negative totals
SELECT id, total FROM orders WHERE total < 0;
elyrasql-cli check --url "$STAGING_URL" checks/

A check that returns rows fails, and the first of those rows are printed. A check can say otherwise with -- @expect: — rows > 0, value >= 100, last_run > '2026-01-01'; see Runbooks. A directive that cannot be read stops its check with exit 2, so a typo never passes silently. Only reads are run: anything else in a check file is refused and reported, never run. --saved also runs the checks saved in ElyraSQL Client for that connection (with --connection).

Migration preflight

elyrasql-cli preflight --url "$STAGING_URL" migrations/

Files are read in name order, and *.down.sql files are skipped unless you pass --include-down. For each statement, preflight reports:

  • NOT NULL, a shorter VARCHAR, a narrower integer, UNIQUE, a primary or foreign key (ALTER TABLE …, CREATE UNIQUE INDEX): how many rows break the rule now. It then says whether the statement will fail, silently change rows, or leave them breaking it. This is the same check the app runs before an ALTER; see Will this ALTER fail on the rows already there?. Any of the three outcomes fails preflight.
  • UPDATE, DELETE, TRUNCATE, DROP TABLE: how many rows it touches. Every row (no WHERE) is a warning. With --max-rows N, more than N rows fails.

Each statement is checked against the database as it is now, not as earlier statements in the same files would leave it. A statement on a table that an earlier migration creates is reported as not checked. Triggers, procedures and views are skipped: they have no rows to count.

Runbooks

elyrasql-cli run --url "$STAGING_URL" runbooks/checkout.elyra.sql

Every block of the runbook runs, top to bottom, and its first rows are printed. A block with -- @expect: passes or fails by it; one without just shows its result. A block that writes is skipped and reported, never run. In GitHub format only failures and skipped blocks are annotated, so a dashboard block does not clutter the pull request.

Output and exit status

Exit Means
0 Everything passed
1 A check returned rows, an @expect did not hold, or a migration would fail or break rows
2 Something could not be run: a bad argument, no connection, an unreadable file, a check the server refused

--format text is the default. --format json is for other tools. --format github writes annotations on the files and lines in the pull request, plus a summary table for the job. It is the default when GITHUB_ACTIONS=true.

GitHub Actions

jobs:
  database:
    runs-on: ubuntu-latest
    env:
      ELYRASQL_CLI_VERSION: "0.19.0"   # pin the release to use
    steps:
      - uses: actions/checkout@v4
      - name: Install elyrasql-cli
        run: |
          base=https://elyracode.com/downloads/sql-client
          file=elyrasql-cli-${ELYRASQL_CLI_VERSION}-linux-x86_64.tar.gz
          curl -fsSLO "$base/$file" -O "$base/$file.sha256"
          sha256sum -c "$file.sha256"
          sudo tar xzf "$file" --strip-components=1 -C /usr/local/bin --wildcards '*/elyrasql-cli'
      - name: Data checks on staging
        run: elyrasql-cli check checks/
        env:
          ELYRASQL_URL: mysql://ci@db.staging:3306/shop
          ELYRASQL_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }}
          ELYRASQL_LICENSE_KEY: ${{ secrets.ELYRASQL_LICENSE_KEY }}
      - name: Preflight the new migrations
        run: elyrasql-cli preflight --max-rows 100000 migrations/
        env:
          ELYRASQL_URL: mysql://ci@db.staging:3306/shop
          ELYRASQL_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }}
          ELYRASQL_LICENSE_KEY: ${{ secrets.ELYRASQL_LICENSE_KEY }}

Use a database user that can read the tables. PROCESS is not needed. A user with no write grants at all is the safest choice, and enough.