mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 21:26:33 +00:00
fix(db): PG adapter coerce 0/1 to boolean for PG strict typing
PG rejects integer values for BOOLEAN columns. B() helper returns 0/1 for SQLite compat. PG adapter now converts 0→false, 1→true in params before sending — safe for both INTEGER and BOOLEAN column types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e71189b874
commit
5cefa04a45
1 changed files with 19 additions and 3 deletions
|
|
@ -59,6 +59,19 @@ export class PgAdapter implements DbAdapter {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private coerceParams(params: ReadonlyArray<SqlValue>): unknown[] {
|
||||||
|
return params.map((v) => {
|
||||||
|
if (v === 0 || v === 1) {
|
||||||
|
// Could be integer or boolean. PG is strict about boolean columns
|
||||||
|
// receiving integer values. We can't know the column type here, but
|
||||||
|
// the `pg` driver accepts JS booleans for both INTEGER and BOOLEAN
|
||||||
|
// columns, so converting 0/1 to false/true is always safe.
|
||||||
|
return v === 1;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async runner<T>(fn: (c: PoolClient) => Promise<T>): Promise<T> {
|
private async runner<T>(fn: (c: PoolClient) => Promise<T>): Promise<T> {
|
||||||
if (this.currentTxClient) return fn(this.currentTxClient);
|
if (this.currentTxClient) return fn(this.currentTxClient);
|
||||||
const client = await this.pool.connect();
|
const client = await this.pool.connect();
|
||||||
|
|
@ -68,8 +81,9 @@ export class PgAdapter implements DbAdapter {
|
||||||
|
|
||||||
async run(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<RunResult> {
|
async run(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<RunResult> {
|
||||||
const pgSql = this.rewriteSql(sql);
|
const pgSql = this.rewriteSql(sql);
|
||||||
|
const pgParams = this.coerceParams(params);
|
||||||
return this.runner(async (c) => {
|
return this.runner(async (c) => {
|
||||||
const res = await c.query(pgSql, params as unknown[]);
|
const res = await c.query(pgSql, pgParams);
|
||||||
let lastInsertRowid = 0n;
|
let lastInsertRowid = 0n;
|
||||||
// If the caller added RETURNING id, pluck it.
|
// If the caller added RETURNING id, pluck it.
|
||||||
if (res.rows.length > 0 && res.rows[0] && "id" in res.rows[0]) {
|
if (res.rows.length > 0 && res.rows[0] && "id" in res.rows[0]) {
|
||||||
|
|
@ -84,16 +98,18 @@ export class PgAdapter implements DbAdapter {
|
||||||
|
|
||||||
async get<T = Row>(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<T | undefined> {
|
async get<T = Row>(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<T | undefined> {
|
||||||
const pgSql = this.rewriteSql(sql);
|
const pgSql = this.rewriteSql(sql);
|
||||||
|
const pgParams = this.coerceParams(params);
|
||||||
return this.runner(async (c) => {
|
return this.runner(async (c) => {
|
||||||
const res = await c.query(pgSql, params as unknown[]);
|
const res = await c.query(pgSql, pgParams);
|
||||||
return (res.rows[0] as T | undefined);
|
return (res.rows[0] as T | undefined);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async all<T = Row>(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<T[]> {
|
async all<T = Row>(sql: string, params: ReadonlyArray<SqlValue> = []): Promise<T[]> {
|
||||||
const pgSql = this.rewriteSql(sql);
|
const pgSql = this.rewriteSql(sql);
|
||||||
|
const pgParams = this.coerceParams(params);
|
||||||
return this.runner(async (c) => {
|
return this.runner(async (c) => {
|
||||||
const res = await c.query(pgSql, params as unknown[]);
|
const res = await c.query(pgSql, pgParams);
|
||||||
return res.rows as T[];
|
return res.rows as T[];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue