Skip to content

Fix BulkUpdate truncating datetime binds to whole seconds#4

Merged
senid231 merged 1 commit into
didww:masterfrom
blurvene:fix-datetime-truncate-ms
Jun 23, 2026
Merged

Fix BulkUpdate truncating datetime binds to whole seconds#4
senid231 merged 1 commit into
didww:masterfrom
blurvene:fix-datetime-truncate-ms

Conversation

@blurvene

Copy link
Copy Markdown

Problem

PgSqlCaller::BulkUpdate#bindings encoded each column's value array with
typecast_array(values, type: <symbol>), which rebuilds the type via
ActiveRecord::Type.lookup(:datetime, array: true) — a generic, non-adapter
type whose PG array text encoder formats Time elements with Time#to_s,
i.e. whole seconds:

  typecast_array(:datetime) → "{\"2026-06-22 16:15:08 UTC\"}"   # .193 dropped
  column-bound serialize     → "2026-06-22 16:15:08.193 UTC"

Two consequences:

  • SET writes stored timestamps truncated to the second.
  • unique_by matches on a timestamp column built WHERE 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_all and the scalar serialize keep precision (connection-bound
quoting); only the array encoding path was lossy.

Fix

bindings now routes datetime/time columns through precise_temporal_array,
formatting each value to a full-microsecond literal (%Y-%m-%d %H:%M:%S.%6N%z,
normalized to UTC — correct for both timestamp and timestamptz). The existing
?::<sql_type>[] cast reparses it losslessly. Non-temporal columns are unchanged.

Tests

  • Exact sub-second round-trip (the existing datetime test used be_within(1),
    which masked the bug).
  • unique_by match 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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_by matching on sub-second datetimes.
  • Bump gem version to 1.1.1 and document the fix in CHANGELOG.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.

Comment thread lib/pg_sql_caller/bulk_update.rb Outdated
Comment thread lib/pg_sql_caller/bulk_update.rb Outdated
Comment thread lib/pg_sql_caller/bulk_update.rb Outdated
Comment thread lib/pg_sql_caller/bulk_update.rb Outdated
Comment thread lib/pg_sql_caller/bulk_update.rb Outdated
@blurvene blurvene force-pushed the fix-datetime-truncate-ms branch from 589b8b6 to 6b824f0 Compare June 23, 2026 07:59
@blurvene blurvene requested a review from senid231 June 23, 2026 08:00
@senid231 senid231 merged commit e7ceb1c into didww:master Jun 23, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants