Semantic Diff

The diff command compares SQL files between git commits and reports semantic changes: tables added/removed, WHERE clauses dropped, JOIN types changed, columns added to SELECT.

Quick Start

# Compare main branch to current HEAD
lexega-sql diff main..HEAD models/ -r

# Compare specific commits
lexega-sql diff abc123..def456 models/ -r

# JSON output for CI/CD
lexega-sql diff main..HEAD -o json

# PostgreSQL projects
lexega-sql diff main..HEAD models/ -r --dialect postgresql

Comparison to git diff

git diff shows text changes:

- SELECT id, name FROM users WHERE active = true
+ SELECT id, name, email FROM users

Semantic diff reports structured change types:

File: models/users.sql
  [HIGH] WHERE clause removed: `active = true`
  [LOW] Column 'email' added

Detected Changes

Change TypeRisk LevelDescription
WhereRemovedHIGHQuery lost filtering — may return unbounded results
WriteStatementBecameUnboundedCRITICALDELETE/UPDATE lost WHERE clause
ColumnAddedLOWNew column in SELECT
JoinTypeChangedCRITICAL/HIGH/MEDIUMCRITICAL: →CROSS JOIN; HIGH: LEFT→INNER; MEDIUM: other
LimitRemovedCRITICAL/HIGHCRITICAL: multi-table (cartesian risk); HIGH: single-table
TableAddedLOWNew table in query
ColumnRemovedLOWColumn removed from SELECT

CI/CD Integration

# GitHub Actions example
name: SQL Semantic Review
on: pull_request

jobs:
  semantic-diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Need full history
      
      - name: Semantic Diff
        run: lexega-sql diff origin/main..HEAD models/ -r --policy .lexega/policy.yml --env prod --decision-out .lexega/
        env:
          LEXEGA_LICENSE_KEY: ${{ secrets.LEXEGA_LICENSE_KEY }}
        # Exit code 2 = blocked by policy, 0 = passed

Jinja/dbt Support

The diff command renders Jinja templates on both sides before comparison, so semantic changes are computed on the rendered SQL, not template source.

Need Help?

Can't find what you're looking for? Check out our GitHub or reach out to support.