Add condition: and set_override: to BulkUpdate, expose #sql#5
Merged
Conversation
`condition:` is a raw-SQL predicate ANDed onto the key match, so a payload is applied only to rows still in the state the caller expects. `set_override:` maps a column to a raw-SQL expression replacing its default `col = v.col` assignment; a key absent from attrs_list appends an assignment of its own, letting a column be written purely from SQL. Both fragments are interpolated verbatim and must qualify their column references with `t.` or `v.` — the two aliases expose the same column names, so an unqualified reference is ambiguous. Validated before any SQL runs: a blank condition, a set_override naming an unknown or unique_by column, and a blank override expression all raise ArgumentError. Rows carrying only unique_by columns no longer raise when set_override supplies the assignments — the guard exists to prevent an empty SET clause, which overrides fill. behavioral context in the spec asserts it as a separate example, so each scenario is covered from both ends; the file documents that convention.
senid231
force-pushed
the
bulk-update-condition-and-set-override
branch
from
July 23, 2026 09:00
0a2af58 to
7048423
Compare
There was a problem hiding this comment.
Pull request overview
Adds optimistic/conditional bulk updates to PgSqlCaller::BulkUpdate by allowing callers to (1) narrow eligible rows via an extra raw-SQL predicate and (2) override per-column SET assignments with raw SQL, plus exposing the generated statement via a new public #sql API.
Changes:
- Add
condition:(extraWHEREpredicate) andset_override:(raw-SQLSETexpressions) toBulkUpdate.call/initialize. - Expose
BulkUpdate#sqlpublicly and expand specs to assert the generated SQL per scenario. - Update README + changelog to document the new options, raw-SQL caveats, and
#sql.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| lib/pg_sql_caller/bulk_update.rb | Adds condition:/set_override: support, new where_clause, enhanced set_clause, and makes #sql public. |
| spec/pg_sql_caller/bulk_update_spec.rb | Adds extensive SQL-shape assertions and new behavioral coverage for condition: and set_override:. |
| README.md | Documents conditional updates, assignment overrides, and public access to generated SQL. |
| CHANGELOG.md | Notes the new keywords and the new BulkUpdate#sql API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
blurvene
reviewed
Jul 23, 2026
blurvene
approved these changes
Jul 23, 2026
`#sql` became public in the previous commit but skipped every validation `#call` runs: a blank `condition` yielded `... AND ()`, a `set_override` naming an unknown or `unique_by` column was emitted verbatim, and an empty `attrs_list` raised NoMethodError on `nil.keys` instead of ArgumentError. Extract `validate!` as the shared entry point of `#call` and `#sql`, and split statement assembly into a private `build_sql` so `#call` reaches it without re-validating. `#call` still validates before its empty-attrs_list short-circuit, so bad options raise even with nothing to update; `#sql` additionally rejects an empty `attrs_list`, which has no statement at all. Coerce `unique_by` to Symbols: it was matched as-is against the payload's Symbol keys, so a String silently failed to exclude its column from the SET clause and escaped the guard against a `set_override` rewriting a match column. Every context where `.call` raises now asserts `#sql` raises the same error.
senid231
force-pushed
the
bulk-update-condition-and-set-override
branch
from
July 23, 2026 11:42
c509228 to
fbc77a7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
condition:is a raw-SQL predicate ANDed onto the key match, so a payload is applied only to rows still in the state the caller expects.set_override:maps a column to a raw-SQL expression replacing its defaultcol = v.colassignment; a key absent from attrs_list appends an assignment of its own, letting a column be written purely from SQL. Both fragments are interpolated verbatim and must qualify their column references witht.orv.— the two aliases expose the same column names, so an unqualified reference is ambiguous.Validated before any SQL runs: a blank condition, a set_override naming an unknown or unique_by column, and a blank override expression all raise ArgumentError. Rows carrying only unique_by columns no longer raise when set_override supplies the assignments — the guard exists to prevent an empty SET clause, which overrides fill.
#sqlis public so the generated statement can be inspected and asserted on. It is the same statement#callruns, so it validates the same inputs:validate!is the shared entry point of both, and statement assembly moved to a privatebuild_sqlthat#callreaches without re-validating.#callstill validates ahead of its empty-attrs_list short-circuit, so bad options raise even with nothing to update;#sqladditionally rejects an empty attrs_list, which has no statement at all. Without this,#sqlhanded back... AND ()for a blank condition, emitted a set_override on an unknown or unique_by column verbatim, and raised NoMethodError onnil.keysfor an empty payload.unique_byis now coerced to Symbols. It was matched as-is against the payload's Symbol keys, so a String silently failed to exclude its column from the SET clause and escaped the guard against a set_override rewriting a match column.Every behavioral context in the spec asserts the exact statement it runs as a separate example, and every context where
.callraises asserts#sqlraises the same error, so each scenario is covered from both ends; the file documents that convention.