PDGGK opened a new pull request, #39611:
URL: https://github.com/apache/shardingsphere/pull/39611

   Changes proposed in this pull request:
   
     - Floor the seconds when converting a date to a protobuf timestamp, so 
pre-epoch values are not shifted forward by a second.
   
   A protobuf `Timestamp` is a second plus a **non-negative** nanosecond offset 
into that second. `converToProtobufTimestamp` derives the seconds with integer 
division, which truncates towards zero, so below the epoch it names the second 
*above* the one the value is in:
   
   ```java
   if (timestamp instanceof Timestamp) {
       Timestamp value = (Timestamp) timestamp;
       return ...newBuilder().setSeconds(value.getTime() / 
1000L).setNanos(value.getNanos()).build();
   }
   long millis = timestamp.getTime();
   return ...newBuilder().setSeconds(millis / 1000L).setNanos((int) ((millis % 
1000L) * 1000000L)).build();
   ```
   
   `java.sql.Timestamp#getNanos` is already the non-negative offset into the 
second the value falls in, so pairing it with a truncated second does not 
describe the same instant:
   
   | input | seconds | nanos | describes | should be |
   |---|---|---|---|---|
   | `new Timestamp(-1500)` | `-1` | `500000000` | `-0.5 s` | `-1.5 s` |
   | `new Timestamp(-1)` | `0` | `999000000` | `+0.999 s` | `-0.001 s` |
   
   Off by a full second, silently, in a CDC stream.
   
   The `java.util.Date` arm is worse — `millis % 1000` keeps the sign, so it 
emits a **negative** nanos:
   
   | input | seconds | nanos |
   |---|---|---|
   | `new Date(-1500)` | `-1` | `-500000000` |
   
   The protobuf `Timestamp` contract requires `0 <= nanos <= 999999999`, so 
that message is invalid; `Timestamps.checkValid` rejects it and a consumer 
decoding it gets whatever its own library does with an out-of-range field.
   
   `Math.floorDiv` and `Math.floorMod` fix both and are identical to `/` and 
`%` at or above the epoch, so nothing else moves.
   
   Any replicated table holding a date before 1970-01-01 is affected — dates of 
birth, historical records, backfilled series.
   
   ### Tests
   
   `assertConvertPreEpochTimestampToTimestampMessage` runs `-1500`, `-1`, 
`-1000`, `-86400000`, `1500` and `0` through both arms and asserts, for each, 
that the nanos are inside `[0, 1000000000)` and that `seconds * 1000 + nanos / 
1000000` comes back as the original millisecond value.
   
   The round trip is deliberate rather than restating the conversion formula — 
the two existing timestamp tests assert against the same expression the 
implementation uses, so they pass either way. On `master` the new test fails on 
the first value:
   
   ```
   
ColumnValueConvertUtilsTest.assertConvertPreEpochTimestampToTimestampMessage:170->assertRoundTrip:180
   Timestamp -1500
   Expected: is <-1500L>
        but: was <-500L>
   ```
   
   `mvn test -pl kernel/data-pipeline/scenario/cdc/core` — 121 tests, all 
passing.
   
   ---
   
   Before committing this PR, I'm sure that I have checked the following 
options:
   - [x] My code follows the [code of 
conduct](https://shardingsphere.apache.org/community/en/involved/conduct/code/) 
of this project.
   - [x] I have self-reviewed the commit code.
   - [ ] I have (or in comment I request) added corresponding labels for the 
pull request.
   - [x] I have passed maven check locally : `./mvnw clean install -B -T1C 
-Dmaven.javadoc.skip -Dmaven.jacoco.skip -e`.
   - [ ] I have made corresponding changes to the documentation.
   - [x] I have added corresponding unit tests for my changes.
   - [ ] I have updated the Release Notes of the current development version. 
For more details, see [Update Release 
Note](https://shardingsphere.apache.org/community/en/involved/contribute/contributor/)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to