Code review now flags SQL that breaks when your schema changes

2 hours ago

Version 14.2.0 teaches /code-review to read your SQL. A new sql-review agent flags statements that work today and break after a harmless schema change, or that run without an error and return the wrong rows. It ships with 17 rules, and it reads SQL files as well as SQL written inside your application code.

Ask about this presentation

Chapters

  1. 0:00Code review reads your SQL
  2. 0:22Positional INSERT
  3. 0:43Valid SQL, wrong rows
  4. 1:04Migrations
  5. 1:28Index-defeating predicates
  6. 1:50SQL inside application code
  7. 2:15What to expect
  8. 2:43Upgrade
Show transcript
  1. Version 14.2.0 teaches /code-review to read your SQL. A new sql-review agent flags statements that work today and break after a harmless schema change, or that run without an error and return the wrong rows. It ships with 17 rules, and it reads SQL files as well as SQL written inside your application code.

    Slide text

    dev-team 14.2.0 · /code-review

    Code review now flags SQL that breaks when the schema changes

    17 rules

    New sql-review agent for .sql files and SQL inside your code

    bdfinst/agentic-dev-team
  2. The lead rule is positional INSERT. The flagged statement has no column list, so values land in columns by position. When a migration adds, drops, or reorders a column, it fails with a column count error or loads a value into the wrong column. The commented-out line is ignored. Naming the columns keeps the statement safe.

    Slide text

    This INSERT breaks the day someone adds a column

    1 -- Seed data for the orders table.
    2 INSERT INTO orders (id, name) VALUES (1, 'alpha');
    3 INSERT INTO orders VALUES (2, 'beta', 'pending');
    4 -- INSERT INTO orders VALUES (3, 'retired')
    5 INSERT INTO orders (id, name) VALUES (4, 'delta');
    warning sql.schema.positional-insert

    Name the columns

    INSERT INTO orders (id, name, status) VALUES (2, 'beta', 'pending');

  3. Two of the correctness rules are errors. Comparing a column to NULL with an equals sign never matches, because the comparison is always unknown. A DELETE with no WHERE clause changes every row in the table. NOT IN over a subquery is a warning. If that subquery returns a single NULL, the whole query returns nothing.

    Slide text

    Valid SQL that returns the wrong rows

    error

    WHERE deleted_at = NULL

    Never matches

    error

    DELETE FROM sessions;

    Deletes every row

    warning

    id NOT IN (SELECT t_id FROM u)

    Returns nothing if u holds one NULL

  4. The migration rules are mostly errors. A rename or drop breaks the previous application version, which keeps running during a rollout and after a rollback, and a drop destroys the data. The fix is to add the new column first, backfill it, and remove the old one in a later release. The same family also catches a new NOT NULL column with no default, and a column narrowed from bigint to int.

    Slide text

    A column drop breaks the app version still running

    error

    ALTER TABLE orders DROP COLUMN qty;

    Expand first

    ALTER TABLE orders ADD COLUMN quantity int;
    -- drop qty in a later release

    NOT NULL without a default bigint to int CREATE INDEX without CONCURRENTLY
  5. The index rules catch predicates that stop the database using an index. Wrapping a column in LOWER means a plain index can't be used, so the query scans the table. Add an expression index, or compare the bare column. A LIKE pattern that starts with a percent sign has no prefix to seek, so the database reads every entry. Use a prefix match instead.

    Slide text

    These predicates scan the table as it grows

    sql.index.function-on-column

    WHERE LOWER(email) = 'a@b.c'

    WHERE email = 'a@b.c'

    sql.index.leading-wildcard

    WHERE name LIKE '%smith'

    WHERE name LIKE 'smith%'

  6. It also finds SQL inside Python, TypeScript, JavaScript, C#, and Java files. It scans string literals, never comments, and only when they look like SQL. Concatenated or interpolated strings can't be scanned as written, so they're counted as skipped. T-SQL and Postgres rules fire only in files of that dialect, which the detector infers, or you set with the dialect flag.

    Slide text

    It reads SQL inside your application code, too

    .py .ts .tsx .js .jsx .cs .java
    skipped, concatenated

    query = "SELECT name FROM users WHERE id = '" + user_id + "'"

    --dialect ansi | tsql | postgres
  7. The notes mark nothing as breaking, but your reviews will change. Whenever a changeset contains SQL, code review now dispatches sql-review. A single error-level hit makes that lens report fail. ORM query builders are out of scope, and injection and N+1 queries stay with the security and performance reviewers. To silence one rule on one statement, add a SQL comment with sql-review, ignore, and the rule id, on the same line or the line before.

    Slide text

    Expect new findings the first time SQL changes

    error

    One error-level hit makes sql-review report fail

    out of scope

    ORM builders, injection, and N+1 queries

    SELECT * FROM orders; -- sql-review: ignore sql.schema.select-star

  8. Upgrading is a version bump in the dev-team plugin, from 14.1.0 to 14.2.0. Wait if a code review verdict gates your merges and your repository holds unreviewed SQL, because the first run can report errors. Otherwise, point code review at a change that touches SQL. It now flags SQL that breaks when the schema changes.

    Slide text

    Upgrade to 14.2.0, then review a change that touches SQL

    plugins/dev-team/.claude-plugin/plugin.json

    - "version": "14.1.0"
    + "version": "14.2.0"

    Wait if

    a code review verdict gates your merges and your SQL is unreviewed

    Code review now flags SQL that breaks when the schema changes.

    bdfinst/agentic-dev-team · dev-team-v14.2.0

What happened in AI today, in your inbox

One short email each evening. Read it and you’re caught up.

Built with ❤️ in New York