#4202 fix: [postgres] advertise scalar columns with native OIDs#4208
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 38 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Code Review - PR #4208: Postgres scalar column OID fidelityOverviewThis PR correctly extends PR #4201's type-fidelity fix from just Bugs introduced by this PR1. DATE binary deserialization - reads 8 bytes after size corrected to 4
case DATE -> new Date(buffer.getLong()); // reads 8 bytes - wrong, binary DATE is int32PostgreSQL binary case DATE -> {
final int daysSince2000 = buffer.getInt(); // 4 bytes
yield new Date((POSTGRES_EPOCH_DAYS + daysSince2000) * 86_400_000L);
}
// with: private static final long POSTGRES_EPOCH_DAYS = 10957L;2. DATE text parser - will throw NumberFormatException for client-sent dates DATE(1082, Date.class, 4, value -> new Date(Long.parseLong(value))),This enum lambda is the client-to-server parser. Before this PR, 3. BOOLEAN text parser - canonical BOOLEAN(16, Boolean.class, 1, value -> value.equalsIgnoreCase("true")),PostgreSQL clients send value -> value.equalsIgnoreCase("true") || value.equals("t")Code quality4. Magic epoch constant as a local variable case TIMESTAMP -> {
final long microseconds = buffer.getLong();
final long POSTGRES_EPOCH_SECONDS = 946684800L; // defined inside switch case
...
}
Test coverage gaps
Minor
SummaryThe direction is right and the fixes for serialization text format are correct. The three bugs above (DATE binary width, DATE text parser, BOOLEAN text parser) are new regressions introduced by adding these types to |
There was a problem hiding this comment.
Code Review
This pull request introduces the TIMESTAMP type and ensures that native scalar types, such as numeric, boolean, and temporal values, are advertised to PostgreSQL clients using their native OIDs instead of being treated as VARCHAR. This change prevents silent type mismatches during round-trips. Key updates include new serialization logic for booleans and dates, and the addition of integration tests for type fidelity. However, the DATE type implementation contains critical bugs: the text parser still expects a long instead of the new YYYY-MM-DD format, and the binary deserializer attempts to read 8 bytes for a field now defined as 4 bytes, which will cause runtime exceptions.
| CHAR(18, Character.class, 1, value -> value.charAt(0)), | ||
| BOOLEAN(16, Boolean.class, 1, value -> value.equalsIgnoreCase("true")), | ||
| DATE(1082, Date.class, 8, value -> new Date(Long.parseLong(value))), | ||
| DATE(1082, Date.class, 4, value -> new Date(Long.parseLong(value))), |
There was a problem hiding this comment.
The textParser for DATE still uses Long.parseLong(value), which will fail now that serializeAsText (line 272) has been updated to produce YYYY-MM-DD strings. This parser must be updated to handle the new date string format. Additionally, note that the binary deserializer in deserializeBinary (line 509) still calls buffer.getLong(), which will cause a BufferUnderflowException now that the DATE size has been reduced to 4 bytes; it should be updated to use buffer.getInt() and handle the PostgreSQL epoch (2000-01-01).
| DATE(1082, Date.class, 4, value -> new Date(Long.parseLong(value))), | |
| DATE(1082, Date.class, 4, value -> java.util.Date.from(java.time.LocalDate.parse(value).atStartOfDay(java.time.ZoneOffset.UTC).toInstant())), |
There was a problem hiding this comment.
Fixed in 27873a4. The DATE textParser now calls parseDateText() which accepts ISO YYYY-MM-DD (Postgres's canonical text form) with an epoch-millis fallback for legacy callers. The binary path also now reads int32 days since 2000-01-01 and converts to Unix epoch via the new POSTGRES_EPOCH_DAYS class constant. Added round-trip tests serializeAsBinaryDateRoundTrip and deserializeTextDateIsoFormat.
| case REAL -> buffer.getFloat(); | ||
| case DOUBLE -> buffer.getDouble(); | ||
| case DATE -> new Date(buffer.getLong()); | ||
| case TIMESTAMP -> { |
There was a problem hiding this comment.
While adding TIMESTAMP support here, please note that the DATE case in the preceding line (line 509) is now incorrect because its size was changed to 4 bytes in this PR (line 56). Currently, it still calls buffer.getLong(), which will cause a BufferUnderflowException when a client sends a 4-byte binary date parameter. It also needs to be adjusted to use the PostgreSQL epoch (2000-01-01) instead of the Java epoch (1970-01-01).
There was a problem hiding this comment.
Fixed in 27873a4. DATE binary deserializer now reads int32 days and converts from 2000-01-01 to a java.util.Date via POSTGRES_EPOCH_DAYS. Updated deserializeBinaryDate test to match. The class-level epoch constants (POSTGRES_EPOCH_SECONDS, POSTGRES_EPOCH_DAYS) replace the previous local final in the TIMESTAMP case.
…N text parser, binary result format Review of PR #4208 surfaced three deserializer regressions introduced by adding DATE, TIMESTAMP, and BOOLEAN to isNativeScalarType() without updating their parsers, plus a stale magic constant. Also adds binary result-format serialization so asyncpg (which requests binary results in Bind for every native OID) works against the newly-advertised column types. - DATE binary deserialization now reads int32 days since 2000-01-01 (was reading 8 bytes as a Unix millis long, which over-read after DATE.size was corrected to 4). - DATE text parser accepts ISO "YYYY-MM-DD" (clients send this format now that DATE is advertised natively) with a legacy epoch-millis fallback. - BOOLEAN text parser accepts t/f/1/0/y/yes/n/no/on/off alongside the verbose forms - asyncpg and pgjdbc both send the canonical single-char form. - POSTGRES_EPOCH_SECONDS / POSTGRES_EPOCH_DAYS hoisted to class-level constants and reused. - PostgresType.serializeAsBinary() added for INT2/4/8, FLOAT4/8, BOOL, CHAR, DATE, TIMESTAMP. - writeRowDescription and writeDataRows now honor portal.resultFormats from the Bind message; falls back to text format when no portal context or when the type has no binary mapping. - e2e-python tests updated to expect native int values for INTEGER/LONG columns (previously the tests asserted string forms because everything was advertised as VARCHAR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed in 27873a4. Going through the items: 1. DATE binary deserialization (8 bytes after size corrected to 4) - Fixed. Reader now consumes 2. DATE text parser ( 3. BOOLEAN text parser ( 4. Magic epoch constant - Hoisted Test coverage gaps - Added: Bonus fix surfaced by python-e2e CI failure - asyncpg requests binary results in Bind for every native OID it has a binary codec for (INT2/4/8, FLOAT4/8, BOOL, DATE, TIMESTAMP, ...). The server was always ignoring Minor items:
Full unit test suite: 168/168 pass. Integration tests: 31 + 72 + 1 = 104/104 pass. |
Review: PR #4208 - Postgres Scalar Column OID FidelityThis PR is well-reasoned and extends the wire-level type fidelity fix from #4201 to all native scalar types. The follow-up fixes in commit Issues1. CHAR binary serialize/deserialize width mismatch (regression - blocks merge)
typeBuffer.putInt(1);
typeBuffer.putByte((byte) c); // 1 byteBut case CHAR -> buffer.getChar(); // buffer.getChar() = 2 bytes in Java (UTF-16 char)This is a real regression introduced by this PR: before it, Fix: change the deserialize side to A test for 2. Misleading comment on TIMESTAMP text format (cosmetic)Line ~300 has the comment Minor observations
What's good
The CHAR binary width mismatch is the only blocker. Everything else is solid. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4208 +/- ##
========================================
Coverage 64.67% 64.68%
========================================
Files 1644 1644
Lines 127266 127408 +142
Branches 27277 27299 +22
========================================
+ Hits 82312 82416 +104
- Misses 33433 33449 +16
- Partials 11521 11543 +22 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…, FLOAT8, FLOAT4, BOOL, TIMESTAMP) Extends PR #4201 which limited the wire-level type fidelity fix to LONG/INT8. PostgresNetworkExecutor.getColumns() now passes through all native scalar types (Integer, Short, Byte, Float, Double, Boolean, Character, Date, LocalDateTime) with their proper Postgres OIDs instead of collapsing to VARCHAR. Clients (pgjdbc, psycopg) use the announced OID to choose a deserializer; VARCHAR for typed columns caused values to round-trip as strings and silently broke typed parameter comparisons. - Added TIMESTAMP (OID 1114) entry for LocalDateTime with text + binary deserialization - getTypeForValue(LocalDateTime) and getTypeFromArcade(DATETIME) now return TIMESTAMP - serializeAsText emits canonical "t"/"f" for Boolean and "YYYY-MM-DD" for Date (was timestamp-format) - DATE size corrected from 8 to 4 to match pg_type binary size - New isNativeScalarType() drives the getColumns() condition Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…N text parser, binary result format Review of PR #4208 surfaced three deserializer regressions introduced by adding DATE, TIMESTAMP, and BOOLEAN to isNativeScalarType() without updating their parsers, plus a stale magic constant. Also adds binary result-format serialization so asyncpg (which requests binary results in Bind for every native OID) works against the newly-advertised column types. - DATE binary deserialization now reads int32 days since 2000-01-01 (was reading 8 bytes as a Unix millis long, which over-read after DATE.size was corrected to 4). - DATE text parser accepts ISO "YYYY-MM-DD" (clients send this format now that DATE is advertised natively) with a legacy epoch-millis fallback. - BOOLEAN text parser accepts t/f/1/0/y/yes/n/no/on/off alongside the verbose forms - asyncpg and pgjdbc both send the canonical single-char form. - POSTGRES_EPOCH_SECONDS / POSTGRES_EPOCH_DAYS hoisted to class-level constants and reused. - PostgresType.serializeAsBinary() added for INT2/4/8, FLOAT4/8, BOOL, CHAR, DATE, TIMESTAMP. - writeRowDescription and writeDataRows now honor portal.resultFormats from the Bind message; falls back to text format when no portal context or when the type has no binary mapping. - e2e-python tests updated to expect native int values for INTEGER/LONG columns (previously the tests asserted string forms because everything was advertised as VARCHAR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
27873a4 to
de5d2c6
Compare
Code ReviewOverviewThis PR correctly extends the PostgreSQL wire-protocol type-fidelity fix from #4201. Instead of only mapping The change is well-motivated and the implementation is mostly solid. A few items worth discussing before merge: Potential Bugs1. TIMESTAMP(1114, LocalDateTime.class, 8, value -> LocalDateTime.parse(value.replace(' ', 'T'))),
2. Negative-epoch pre-2000 timestamps in binary deserialization are correct, but not tested The nanos-correction block in int nanos = (int) (microseconds % 1_000_000L) * 1000;
if (nanos < 0) {
secs -= 1;
nanos += 1_000_000_000;
}The arithmetic is correct, but there is no test for a pre-2000 timestamp (negative 3. When a column value is null, returning 4. The comment says "legacy callers" but there is no code path that would produce a bare epoch-millis string under the new wire format. If it is truly dead, remove it. If it must stay for safety, a log warning would help diagnose unexpected inputs rather than silently producing a wrong date. Minor Issues5. The method is only used within 6. typeBuffer.putByte((byte) c);PostgreSQL OID 18 ( 7. Column-index comment style in The comment block removed at line 117-118 was the only documentation of the format-code field. The new comment is good, but it would be even clearer to note which Postgres message spec paragraph this comes from (Frontend/Backend Protocol - RowDescription). Test CoverageGood additions:
Missing:
PerformanceNo concerns. The SummaryThe core approach is correct and the fix is necessary for proper Postgres client compatibility. The main items to address before merge are the 🤖 Generated with Claude Code |
… parser hardening Address PR #4208 review feedback (May 11 + May 19) and python-e2e failure surfaced in CI run 26117714838: PostgresType.java - serializeAsBinary defensive coercion for SMALLINT/INTEGER/LONG/REAL/DOUBLE/ BOOLEAN/DATE/TIMESTAMP via toNumber/toBooleanValue/toLocalDateValue/ toLocalDateTimeValue helpers. Schemaless documents can hold mixed runtime types per column; the wire announces one format code per column for the whole result, so the serializer must coerce rather than throw. Fixes the ClassCastException seen in e2e-python test_parameterized_insert when value was stored as String "300" but column was announced as INT8 from a prior row's Long. - CHAR binary deserializer reads 1 byte (Postgres "char" OID 18 is single byte) to match serializer; previously read 2 bytes via buffer.getChar(). - TIMESTAMP text parser now accepts ISO_LOCAL_DATE_TIME plus ISO_OFFSET_DATE_TIME suffix (e.g. "2024-06-15 12:30:45+00:00"). - parseBooleanText throws on null and unknown values; accepts canonical t/f/true/false/0/1/y/n/yes/no/on/off only. - parseDateText: removed dead Long.parseLong legacy fallback. - TIMESTAMP text-format comment corrected from "HH:MM:SS.ffffff" (months) to "HH:mm:ss.SSSSSS" (minutes). PostgresTypeTest.java (+21 tests, 204 unit tests pass) - deserializeBinaryChar updated to 1-byte spec; serializeAsBinaryCharRoundTrip added. - serializeAsBinary{Integer,Long,Double,Boolean,Date,Timestamp}Coerces* cover mixed-runtime-type round-trips. - serializeAsBinaryTimestampPre2000RoundTrip exercises the nanos<0 borrow branch in deserializeBinary. - parseBooleanText{NullThrows,AcceptsCanonicalForms,RejectsUnknown} + parseTimestampTextAcceptsOffset + parseDateTextRejectsNonIso. e2e-python - # nosec B101 on the two delta-modified asserts to clear Codacy churn (Bandit flags assert in compiled bytecode; pytest convention).
Code ReviewOverviewThis PR extends the PostgreSQL wire-level type fidelity fix from #4201 (which only covered The change is well-motivated: Postgres clients (pgjdbc, psycopg) select a deserializer from the announced OID, so advertising Positive Aspects
Issues1. Timezone-sensitive assertion in assertThat(ts.toString()).startsWith("2024-06-15 12:30:45");
assertThat(ts.toLocalDateTime()).isEqualTo(LocalDateTime.of(2024, 6, 15, 12, 30, 45));2. Missing st.execute("CREATE VERTEX TYPE SCALAR_COL_TYPES");If the type already exists (e.g., from a previous failed run that left state behind), this throws and the test fails with a confusing error. Add st.execute("CREATE VERTEX TYPE IF NOT EXISTS SCALAR_COL_TYPES");(The 3. Breaking change in DATE binary deserialization The old 4. Arrays sent as text when binary format is requested default -> serializeAsText(pgType, typeBuffer, value);When a client sends a Bind message requesting binary result formats (
5. Nano precision loss in TIMESTAMP serialization final long secsFromPgEpoch = ldt.toEpochSecond(ZoneOffset.UTC) - POSTGRES_EPOCH_SECONDS;
typeBuffer.putLong(secsFromPgEpoch * 1_000_000L + ldt.getNano() / 1000L);
Minor Notes
SummaryThis is a solid, protocol-compliant fix with good coverage. The four items above are worth addressing before merge, with items 1, 2, and 4 being the most important to fix. |
|
Round 3 review-iteration in Python-e2e Root cause: CHAR binary width mismatch (
TIMESTAMP text parser (May 19 review #1) Promoted the inline lambda to
Now throws
Removed. There are no live callers; per CLAUDE.md "no speculative code." New Pre-2000 TIMESTAMP binary round-trip (May 19 review #2)
Comment fix (
Visibility / overloads (May 19 review #5)
Codacy 2 high-severity findings Both are Bandit Test totals: 204 unit tests pass (was 183), +21 new/updated. Postgresw IT: 104 pass, 1 pre-existing skip. CI status on Tracked as follow-up (out of scope, deliberately)
|
…t fragility claude[bot] PR #4208 review feedback (May 19): PostgresType / PostgresNetworkExecutor (item 4 - protocol violation) - Add PostgresType.hasBinaryEncoding(): false for ARRAY_* (no binary encoder implemented), true for everything else including VARCHAR/TEXT/BPCHAR/JSON where text and binary wire bytes are identical. - Add PostgresNetworkExecutor.effectiveResultFormat() that forces text format (0) for columns whose type lacks a binary encoder, regardless of what the client requested in Bind. - writeRowDescription and writeDataRows now go through effectiveResultFormat so the announced format code in RowDescription always agrees with the bytes written in DataRow. Previously, a client requesting binary on an array column got "binary" announced but text bytes - a protocol violation that would mis-decode on the client side. PostgresTypeTest (items 4 + 5) - hasBinaryEncodingScalarTypesTrue, hasBinaryEncodingArrayTypesFalse cover the new helper. - serializeAsBinaryTimestampTruncatesSubMicrosecondNanos documents that Postgres timestamp is microsecond-resolution by design - input 123_456_789 nanos round-trips as 123_456_000. PostgresWJdbcIT (items 1 + 2) - temporalColumnTypeFidelity asserts ts.toLocalDateTime() instead of ts.toString().startsWith(...) - Timestamp.toString() formats in the JVM default timezone and would fail on non-UTC hosts. - scalarColumnTypeFidelity and temporalColumnTypeFidelity now use the CREATE VERTEX TYPE <name> IF NOT EXISTS suffix syntax (ArcadeDB's grammar puts IF NOT EXISTS after the type name, not before). PostgresType (minor alignment) - Removed extra spaces around the = on POSTGRES_EPOCH_DAYS to match the surrounding constant declarations. Item 3 (DATE binary wire-format change) intentionally not changed: the old 8-byte epoch-millis path was a protocol-spec violation. Any compliant client followed Postgres OID 1082 = int32 days since 2000-01-01, which is what the new code does. There is no compatibility shim to write. Tests: 207 unit tests pass (+3), 104 IT pass (1 pre-existing skip).
|
Round-4 fixes in Item 4 (array + binary format mismatch — protocol violation, real bug) Verified against the code. Fix: added Tests Item 5 (sub-microsecond truncation documentation) Added Item 1 (Timestamp.toString timezone-sensitive assertion) Switched Item 2 ( Added Item 3 (DATE binary wire-format change) — pushing back Calling this a "wire-level compatibility break worth a migration note" overstates it. The old code read Minor alignment Removed the extra spaces around Self-passing convention Acknowledged - pre-existing pattern, intentionally kept as-is. Results 207 unit tests pass (+3 over previous round), 104 IT pass (1 pre-existing skip). |
Code ReviewOverviewThis PR extends the PostgreSQL wire-protocol type-fidelity work from #4201, advertising all native scalar types (numeric, boolean, temporal) with proper Postgres OIDs instead of collapsing to VARCHAR. It also adds binary response support keyed off Bind-message format codes, and fixes pre-existing bugs in DATE/BOOL serialization. The approach is correct and the fix is a real protocol compliance improvement - clients that depend on OID-based deserializer selection will silently break without this. CorrectnessDate/Timestamp epoch math - correct. CHAR binary fix - fixing from DATE size -
Sub-microsecond truncation - the test IssuesStyle violations (CLAUDE.md: "don't use fully qualified names if possible, always import the class and just use the name")
// toNumber()
return new java.math.BigDecimal(value.toString()); // should be: import java.math.BigDecimal
// parseTimestampText()
return java.time.OffsetDateTime.parse(iso).toLocalDateTime(); // should be: import java.time.OffsetDateTimeBoolean text parsing - case sensitivity gap
|
Code Review - PR #4208: Postgres native OID advertising for all scalar typesOverviewThis PR correctly generalizes the type-fidelity fix from PR #4201 (LONG-only) to all native scalar types. The core problem is real: advertising What the previous Claude review flagged (now fixed)The existing comment on this PR identified three bugs in an earlier iteration. This diff addresses all of them:
Remaining issues1. Static import of import static java.time.OffsetDateTime.parse;
// ...
return parse(iso).toLocalDateTime();The bare 2. if (value instanceof String s)
return parseDateText(s).toInstant().atZone(ZoneOffset.UTC).toLocalDate();
if (value instanceof String s)
return LocalDate.parse(s);3. Both Correctness highlights
Test coverageExcellent. The PR adds:
The coercion tests for schemaless documents (a string arriving in a column declared as INTEGER) are a nice defensive addition. SummaryThe three bugs from the previous review are fixed and the implementation is correct. Issues 1 and 2 are minor quality improvements; issue 3 is the only one that could cause a latent bug if the map type ever changes. Overall this is solid work and in good shape to merge after addressing the static import and the ordering invariant documentation. Generated with Claude Code |
Code ReviewOverviewThis PR extends the PostgreSQL wire-level type fidelity work from #4201, advertising all native scalar types (numeric, boolean, temporal) with their correct Postgres OIDs instead of collapsing to VARCHAR. It also adds proper TIMESTAMP support for LocalDateTime/DATETIME, fixes the BOOL canonical text format, corrects the DATE binary wire size (8 -> 4 bytes), and introduces binary format negotiation from Bind message result-format codes. The changes are well-scoped and the test coverage is comprehensive. Correctness - All Good
Issues and Suggestions1. OID 18 is Postgres's internal "char" type (single-byte, not 2. Nanos multiplication silently truncates as // PostgresNetworkExecutor line ~451 (deserializeBinary TIMESTAMP)
int nanos = (int) (microseconds % 1_000_000L) * 1000;The cast to int nanos = (int) ((microseconds % 1_000_000L) * 1000L);
// or
long microRemainder = microseconds % 1_000_000L;
int nanos = (int) (microRemainder * 1000L);3. The refactored switch: return switch (val) {
case int[] ints -> PostgresType.ARRAY_INT;
...
default -> throw new IllegalStateException("Unexpected value: " + val);
};The old chain fell through to 4. Breaking change to DATE text deserialization - document it at the caller level // was: value -> new Date(Long.parseLong(value))
// now: value -> parseDateText(value) (ISO "YYYY-MM-DD" only)The test 5.
Test CoverageCoverage is excellent:
The removal of Minor Nits
SummaryThe implementation is correct, the protocol constants are verified, and the test suite is thorough including edge cases (pre-2000 dates, negative nanos, coercion). The main action items before merging are:
🤖 Generated with Claude Code |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 44 |
🟢 Coverage 76.39% diff coverage · -7.47% coverage variation
Metric Results Coverage variation ✅ -7.47% coverage variation Diff coverage ✅ 76.39% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (d4c3c5a) 127266 93862 73.75% Head commit (1e89810) 159086 (+31820) 105451 (+11589) 66.29% (-7.47%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#4208) 144 110 76.39% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Code Review - PR #4208Correctness VerificationThe implementation is correct on the current diff. Key fixes confirmed:
Issues Worth Addressing1. CHAR (OID 18) in OID 18 is Postgres's internal single-byte 2. default -> throw new IllegalStateException("Unexpected value: " + val);Previously, unknown array types (e.g., 3. Nanos cast - precedence is safe but subtle int nanos = (int) (microseconds % 1_000_000L) * 1000;The int nanos = (int) ((microseconds % 1_000_000L) * 1000L);4. Both methods iterate the same Minor
Test CoverageExcellent: binary round-trips for every new scalar type, pre-2000 timestamps, sub-microsecond truncation, coercion from string/numeric mismatches (schemaless scenario), and two new integration tests covering both value-based and schema-based column-type-announcement paths. Removal of The two items most worth resolving before merge are #1 (CHAR OID mapping may surprise pgjdbc callers) and #2 (array Generated with Claude Code |
Summary
LONG/INT8.PostgresNetworkExecutor.getColumns()now passes through all native scalar types with their proper Postgres OIDs instead of collapsing toVARCHAR.TIMESTAMP(OID 1114)PostgresTypeentry soLocalDateTimevalues (andDATETIMEschema columns) round-trip as native timestamps, not date strings.BOOLnow uses canonicalt/f,DATEis emitted asYYYY-MM-DD(was timestamp format), and theDATEsize field is corrected to 4 bytes.Why
Postgres clients (pgjdbc, psycopg) choose a deserializer based on the announced OID. Advertising
VARCHARfor typed scalar columns causes values to round-trip as strings - the exact class of bug fixed forLong/id()in #4201 - which silently breaks numeric, boolean, and temporal parameter comparisons against client-side types.Changes
postgresw/src/main/java/com/arcadedb/postgres/PostgresType.javaTIMESTAMP(1114, LocalDateTime.class, 8, ...)enum entry with text parser and binary (microseconds since 2000-01-01T00:00:00Z) deserializationgetTypeForValue(LocalDateTime)->TIMESTAMP(wasDATE)getTypeFromArcade(DATETIME)->TIMESTAMP(wasDATE)serializeAsTextforBooleanemits canonicalt/fserializeAsTextforDateemitsYYYY-MM-DD(was timestamp format, wrong for DATE OID 1082)DATE.sizecorrected from 8 to 4 (matches pg_type binary size)isNativeScalarType()helperpostgresw/src/main/java/com/arcadedb/postgres/PostgresNetworkExecutor.javagetColumns()condition changed frompgType.isArrayType() || pgType == LONGtopgType.isArrayType() || pgType.isNativeScalarType()- covers SMALLINT, INTEGER, LONG, REAL, DOUBLE, CHAR, BOOLEAN, DATE, TIMESTAMPTest plan
PostgresWJdbcIT.scalarColumnTypeFidelity(new) - Cypher RETURN path assertsint4/float8/float4/boolcolumn-type announcement and round-trip valuesPostgresWJdbcIT.temporalColumnTypeFidelity(new) - schema-based SELECT assertstimestampcolumn type andTimestampround-tripPostgresTypeTest(160 unit tests, 4 updated to reflect new behavior)PostgresWJdbcIT(31 tests pass, 1 pre-existing skip)PostgresProtocolIT+TomcatConnectionPoolPostgresWJdbcIT(73 tests pass)dateTimeSerializationFormat,queryVertices)Closes #4202
🤖 Generated with Claude Code