Fix BulkUpdate truncating datetime binds to whole seconds#4
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes PgSqlCaller::BulkUpdate losing sub-second precision when binding datetime/time column arrays, which could both truncate stored timestamps and cause unique_by matches on sub-second keys to miss rows.
Changes:
- Route temporal columns through a custom array-literal encoder that preserves microseconds.
- Tighten specs to assert exact microsecond preservation and
unique_bymatching on sub-second datetimes. - Bump gem version to
1.1.1and document the fix inCHANGELOG.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
lib/pg_sql_caller/bulk_update.rb |
Adds temporal-aware array encoding to preserve microsecond precision for datetime/time binds. |
spec/pg_sql_caller/bulk_update_spec.rb |
Adds regression specs for microsecond round-trip and unique_by matching on sub-second datetimes. |
lib/pg_sql_caller/version.rb |
Bumps version from 1.1.0 to 1.1.1. |
CHANGELOG.md |
Adds a 1.1.1 entry describing the temporal array precision fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
senid231
reviewed
Jun 23, 2026
589b8b6 to
6b824f0
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.
Problem
PgSqlCaller::BulkUpdate#bindingsencoded each column's value array withtypecast_array(values, type: <symbol>), which rebuilds the type viaActiveRecord::Type.lookup(:datetime, array: true)— a generic, non-adaptertype whose PG array text encoder formats
Timeelements withTime#to_s,i.e. whole seconds:
Two consequences:
unique_bymatches on a timestamp column builtWHERE ts = '…:08'(
.000), which never equals a stored sub-second row → zero rows matched,silent no-op (no error, returns 0).
Not a precision-of-type issue and not a timezone shift — AR's own
where/insert_alland the scalarserializekeep precision (connection-boundquoting); only the array encoding path was lossy.
Fix
bindingsnow routesdatetime/timecolumns throughprecise_temporal_array,formatting each value to a full-microsecond literal (
%Y-%m-%d %H:%M:%S.%6N%z,normalized to UTC — correct for both
timestampandtimestamptz). The existing?::<sql_type>[]cast reparses it losslessly. Non-temporal columns are unchanged.Tests
be_within(1),which masked the bug).
unique_bymatch on a sub-second datetime key.Both verified red without the fix, green with. Full suite: 65 examples, 0 failures.
Compatibility
Bug fix only, no API change. Bumped to 1.1.1.