From 50db8afe6af7d62549f360ccb318702e3f159bee Mon Sep 17 00:00:00 2001 From: akarma-synetal Date: Fri, 10 Jul 2026 19:29:54 +0530 Subject: [PATCH] fix: correct JSON field serialization for PostgreSQL vs SQLite PostgreSQL native jsonb columns require all values to be valid JSON, while SQLite stores JSON as TEXT so only objects/arrays need stringification. Previously the SQLite-only path incorrectly applied to PG, and primitives in PG jsonb columns were silently rejected. Co-authored-by: Cursor --- packages/plugins/driver-sql/src/sql-driver.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/plugins/driver-sql/src/sql-driver.ts b/packages/plugins/driver-sql/src/sql-driver.ts index 80a1b397e1..6b0b2b836a 100644 --- a/packages/plugins/driver-sql/src/sql-driver.ts +++ b/packages/plugins/driver-sql/src/sql-driver.ts @@ -3162,18 +3162,33 @@ export class SqlDriver implements IDataDriver { } } - if (!this.isSqlite) return copy; - - const fields = this.jsonFields[object]; - if (fields && fields.length > 0) { - if (!copied) { copy = { ...copy }; copied = true; } - for (const field of fields) { - if (copy[field] !== undefined && typeof copy[field] === 'object' && copy[field] !== null) { + // JSON field serialisation: PostgreSQL native jsonb columns require + // valid JSON for ALL values (strings, numbers, booleans, objects). + // SQLite stores JSON as plain TEXT so only objects/arrays need + // stringification (better-sqlite3 can only bind primitives). + const jsonFields = this.jsonFields[object]; + if (jsonFields && jsonFields.length > 0) { + for (const field of jsonFields) { + if (copy[field] === undefined || copy[field] === null) continue; + if (this.isSqlite) { + // SQLite: only objects/arrays need JSON.stringify; primitives + // are stored as-is and re-parsed on read by formatOutput. + if (typeof copy[field] === 'object') { + if (!copied) { copy = { ...copy }; copied = true; } + copy[field] = JSON.stringify(copy[field]); + } + } else { + // PostgreSQL: every value must be valid JSON so the native + // jsonb column accepts it. JSON.stringify wraps strings in + // quotes, leaves numbers/booleans unchanged as literals. + if (!copied) { copy = { ...copy }; copied = true; } copy[field] = JSON.stringify(copy[field]); } } } + if (!this.isSqlite) return copy; + // Safety net: better-sqlite3 can only bind numbers/strings/bigints/buffers/ // null. Any value still an array or plain object here (a field type not // classified as JSON, a `Field.multiple` we didn't catch, or an ad-hoc