Repository navigation
fix(postgres): show real values without float noise and keep row matching - #950
Conversation
Code Review SummaryStatus: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (8 files)
Fix these issues in Kilo Cloud Previous Review Summaries (2 snapshots, latest commit 68c4948)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 68c4948)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (9 files)
Fix these issues in Kilo Cloud Previous review (commit 18311fd)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (8 files)
Reviewed by free · Input: 203.7K · Output: 6.1K · Cached: 349.9K |
| const col = quoteIdentifier(fk.ref_column, driver); | ||
| return `${col} = ${formatSqlValueForFilter(value, sourceColumnType)}`; | ||
| const literal = formatSqlValueForFilter(value, sourceColumnType); | ||
| const isPostgresReal = shouldQuoteIdentifiers(driver) && |
There was a problem hiding this comment.
SUGGESTION: Uses shouldQuoteIdentifiers(driver) as PostgreSQL dialect proxy
shouldQuoteIdentifiers(driver) returns true for any dialect that uses double-quote identifiers (PostgreSQL, SQLite, etc.). For precision, prefer checking driver.capabilities?.sql_dialect === "postgres" (or driver.sql_dialect === "postgres" for string drivers) to specifically target PostgreSQL.
| const isPostgresReal = shouldQuoteIdentifiers(driver) && | |
| const isPostgresReal = driver.capabilities?.sql_dialect === "postgres" && | |
| /^(real|float4)$/i.test(sourceColumnType?.trim() ?? ""); |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| Ok(Some(t)) => { | ||
| types.insert(col.clone(), t); | ||
| } | ||
| Ok(None) => log::warn!( |
There was a problem hiding this comment.
SUGGESTION: Ok(None) (no column type found) logs at warn level
When get_column_data_type returns Ok(None) (column exists but type couldn't be determined), it logs at warn level. This is expected for some schemas (e.g., views, temporary tables) and could be debug instead. Keep warn for the Err case (actual failures).
| Ok(None) => log::warn!( | |
| Ok(None) => log::debug!( |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
|
@verbaux — really appreciate you tackling this across both repos. The float noise issue is a real user-facing pain point and this is a thorough, well-researched fix — the midpoint handling in the float4 extraction is a particularly nice piece of work, and the FK navigation CAST in We ran the same two independent review passes as on the plugin PR (#144) — an 11-dimension ensemble pre-handoff review and a focused code-review — and aggregated the results. Here's the summary: Blocking (must address before merge)1.
|
debba
left a comment
There was a problem hiding this comment.
Thanks a lot for this! The midpoint guard in the FLOAT4 decoder (if decimal as f32 == v { decimal } else { f64::from(v) }) is a really careful detail, and I like that you fixed the predicate side too instead of only cleaning up the display. The parity test with single and composite real keys, midpoints included, gives me a lot of confidence in the UPDATE/DELETE path.
There's one regression I'd like fixed before merging:
Filter by cell value on a real column no longer matches. buildCellValueFilterClause in src/utils/cellValueFilter.ts (the "filter by this value" context menu) still builds "price" = 89.9 with no cast. Postgres resolves real = 89.9 as a float4/float8 comparison, so it's false. I checked on Postgres 16: SELECT 89.9::real = 89.9 returns f, while 89.9::real = 89.9000015258789 and 89.9::real = CAST(89.9 AS real) both return t. Before this PR the cell carried 89.9000015258789, so = matched; now = returns no rows and <> returns every row, including the one you clicked. Could you apply the same CAST(... AS real) there as in buildForeignKeyFilterClause? Ideally both go through one small shared helper, with a test in tests/utils/cellValueFilter.test.ts.
Two small optional things:
- In
extract/simple.rsthe FLOAT4 conversion could be a small named helper (something likefloat4_to_json_f64) instead of living inline in the match arm. It reads a bit better and fits.rules/rust.md. - In
foreignKeys.ts,shouldQuoteIdentifiers(driver)works as a Postgres dialect check, but the name says something else. A named check (or the shared helper above) would make the intent clearer.
|
@debba @aesslinger thanks for the reviews. Pushed:
|
debba
left a comment
There was a problem hiding this comment.
Hey @verbaux, thanks for turning this around so quickly. Moving both the FK navigation and the "filter by this value" path onto the same formatSqlValueForColumnComparison helper is exactly what I had in mind, and the new cases in tests/utils/cellValueFilter.test.ts cover the parts I cared about: = and <>, number and string values, double precision and MySQL left alone, and IS NULL untouched. I also appreciate that you closed the string identity gap in build_pk_predicate while you were at it, with the keyless parity test on top.
The two optional points are done as well: float4_to_json_f64 reads much better than the inline match arm, and isPostgresDialect makes the intent in foreignKeys.ts obvious.
I ran it on the branch: cargo test --lib drivers::postgres gives 290 passed, the cellValueFilter and foreignKeys suites pass (21 and 22), and CI is green including the Postgres integration job. Nothing left on my side, merging. Then the plugin PR can follow as you planned.
Refs #906
What
PostgreSQL
real(float4) values reached the grid widened to f64, so89.9showed as89.9000015258789.89.9). For the two f32 values whose shortest decimal parses to an exact f32 midpoint (±7.038531e-26), the exact widened value is used instead, so the JSON number always converts back to the stored f32. FLOAT8, NULL and NaN/Infinity (stillnull) are unchanged. Arrays, domains and composites go through the same scalar path.89.9back, andreal_col = 89.9::double precisionmatches nothing. When the column type is REAL/FLOAT4, the WHERE value is now cast toreal(CAST(CAST($n AS double precision) AS real)). This covers UPDATE, DELETE and BLOB lookup, including composite keys and tables without a primary key.realFK column usesCAST(<value> AS real)for the same reason.The standalone PostgreSQL plugin gets the same fix on this branch.
MySQL
FLOATwas separately confirmed to have the same display problem through a different code path; it is outside this PR.Verification
cargo test --lib drivers::postgres: 290 passed, 1 ignored.parity_real_primary_keys_round_trip_through_jsonagainst the plugin built from the branch above, PostgreSQL 18: single and compositerealkeys, including the midpoint values. Full parity run: 84 passed.pnpm exec vitest run tests/utils/foreignKeys.test.ts: 22 passed.