Skip to content

Commit 8558bb8

Browse files
committed
fix: bigint precision in SQL evaluator cross-type comparison + CAST BIGINT
- looseEqual/compare: prefer BigInt conversion when Number.isSafeInteger to avoid precision loss for values > 2^53 - CAST(x AS BIGINT) now returns actual bigint instead of truncated number
1 parent 33c8fdd commit 8558bb8

File tree

1 file changed

+8
-5
lines changed

1 file changed

+8
-5
lines changed

src/sql/evaluator.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,19 @@ function looseEqual(a: unknown, b: unknown): boolean {
159159
if (typeof a === "bigint" && typeof b === "bigint") return a === b;
160160
if (typeof a === "string" && typeof b === "string") return a === b;
161161
if (typeof a === "boolean" && typeof b === "boolean") return a === b;
162-
// Cross-type numeric comparison
163-
if (typeof a === "number" && typeof b === "bigint") return a === Number(b);
164-
if (typeof a === "bigint" && typeof b === "number") return Number(a) === b;
162+
// Cross-type numeric: prefer bigint when safe to avoid precision loss
163+
if (typeof a === "number" && typeof b === "bigint") return Number.isSafeInteger(a) ? BigInt(a) === b : a === Number(b);
164+
if (typeof a === "bigint" && typeof b === "number") return Number.isSafeInteger(b) ? a === BigInt(b) : Number(a) === b;
165165
return a === b;
166166
}
167167

168168
function compare(a: unknown, b: unknown): number {
169169
if (typeof a === "number" && typeof b === "number") return a - b;
170170
if (typeof a === "bigint" && typeof b === "bigint") return a < b ? -1 : a > b ? 1 : 0;
171171
if (typeof a === "string" && typeof b === "string") return a.localeCompare(b);
172-
// Cross-type: coerce to number
172+
// Cross-type bigint/number: prefer bigint when safe
173+
if (typeof a === "bigint" && typeof b === "number" && Number.isSafeInteger(b)) { const bb = BigInt(b); return a < bb ? -1 : a > bb ? 1 : 0; }
174+
if (typeof a === "number" && typeof b === "bigint" && Number.isSafeInteger(a)) { const ab = BigInt(a); return ab < b ? -1 : ab > b ? 1 : 0; }
173175
return toNumber(a) - toNumber(b);
174176
}
175177

@@ -188,7 +190,8 @@ function matchLike(value: string, pattern: string): boolean {
188190
function castValue(val: unknown, targetType: string): unknown {
189191
if (val === null) return null;
190192
const t = targetType.toLowerCase();
191-
if (t === "int" || t === "integer" || t === "bigint") return Math.trunc(toNumber(val));
193+
if (t === "bigint") return BigInt(Math.trunc(toNumber(val)));
194+
if (t === "int" || t === "integer") return Math.trunc(toNumber(val));
192195
if (t === "float" || t === "double" || t === "real" || t === "decimal" || t === "numeric") return toNumber(val);
193196
if (t === "text" || t === "varchar" || t === "string" || t === "char") return String(val);
194197
if (t === "bool" || t === "boolean") return isTruthy(val);

0 commit comments

Comments
 (0)