From e2f1f21bbd3dcceaa45917ada80d60a5f0ff85fd Mon Sep 17 00:00:00 2001 From: Vaibhav Bhalla Date: Tue, 20 Jan 2026 10:01:30 +0530 Subject: [PATCH] feat: add operator support for partial json update Signed-off-by: Vaibhav Bhalla --- lib/sql.js | 2 +- test/sql.test.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/lib/sql.js b/lib/sql.js index e4479c19..1bf6a3f8 100644 --- a/lib/sql.js +++ b/lib/sql.js @@ -1326,7 +1326,7 @@ SQLConnector.prototype._buildFieldsForKeys = function(model, data, keys, exclude continue; } const k = this.columnEscaped(model, key); - const v = this.toColumnValue(p, data[key]); + const v = this.toColumnValue(p, data[key], undefined, k); if (v !== undefined) { fields.names.push(k); if (v instanceof ParameterizedSQL) { diff --git a/test/sql.test.js b/test/sql.test.js index 2fd635ea..d932a836 100644 --- a/test/sql.test.js +++ b/test/sql.test.js @@ -626,4 +626,101 @@ describe('sql connector', function() { }); }); }); + describe('toColumnValue with column name parameter', function() { + it('should pass escaped column name as fourth parameter', function() { + let capturedColumnName; + const originalToColumnValue = connector.toColumnValue; + // Override toColumnValue to capture the fourth parameter + connector.toColumnValue = function(prop, val, escaping, columnName) { + capturedColumnName = columnName; + return originalToColumnValue.call(this, prop, val, escaping); + }; + + try { + const fields = connector.buildFields('customer', { + name: 'John', + vip: true, + }); + // Verify that the escaped column name was passed + expect(capturedColumnName).to.be.oneOf(['`NAME`', '`VIP`']); + } finally { + // Restore original method + connector.toColumnValue = originalToColumnValue; + } + }); + + it('should enable partial JSON updates with column-specific logic', function() { + const originalToColumnValue = connector.toColumnValue; + // Mock connector that handles JSON columns differently + connector.toColumnValue = function(prop, val, escaping, columnName) { + // Example: Enable partial JSON update for specific columns + if (columnName === '`ADDRESS`' && typeof val === 'object' && val !== null) { + // For JSON columns, connector can use database-specific JSON update syntax + // e.g., PostgreSQL: column = column || '{"new": "data"}'::jsonb + // This demonstrates how the column name enables column-specific handling + return { + sql: 'jsonb_set(' + columnName + ', ?, ?)', + params: ['{path}', JSON.stringify(val)], + }; + } + return originalToColumnValue.call(this, prop, val, escaping); + }; + + try { + const fields = connector.buildFields('customer', { + address: {city: 'Miami', zip: '33101'}, + }); + // Verify that column-specific logic was applied + expect(fields.columnValues).to.have.lengthOf(1); + expect(fields.columnValues[0].toJSON()).to.deep.include({ + sql: 'jsonb_set(`ADDRESS`, ?, ?)', + }); + } finally { + connector.toColumnValue = originalToColumnValue; + } + }); + it('should work with buildFieldsForUpdate', function() { + const capturedColumnNames = []; + const originalToColumnValue = connector.toColumnValue; + connector.toColumnValue = function(prop, val, escaping, columnName) { + if (columnName) { + capturedColumnNames.push(columnName); + } + return originalToColumnValue.call(this, prop, val, escaping); + }; + + try { + connector.buildFieldsForUpdate('customer', { + lastName: 'Doe', + vip: true, + }); + // Verify column names were passed for UPDATE operations + expect(capturedColumnNames).to.include.members(['`LASTNAME`', '`VIP`']); + } finally { + connector.toColumnValue = originalToColumnValue; + } + }); + + it('should work with buildFieldsFromArray for bulk inserts', function() { + const capturedColumnNames = []; + const originalToColumnValue = connector.toColumnValue; + connector.toColumnValue = function(prop, val, escaping, columnName) { + if (columnName && !capturedColumnNames.includes(columnName)) { + capturedColumnNames.push(columnName); + } + return originalToColumnValue.call(this, prop, val, escaping); + }; + + try { + connector.buildFieldsFromArray('customer', [ + {name: 'John', vip: true}, + {name: 'Jane', vip: false}, + ]); + // Verify column names were passed for all rows + expect(capturedColumnNames).to.include.members(['`NAME`', '`VIP`']); + } finally { + connector.toColumnValue = originalToColumnValue; + } + }); + }); });