From 583887c43cb2c24d36222ae44810abe726f5d5e2 Mon Sep 17 00:00:00 2001 From: Kulshekhar Kabra Date: Wed, 2 Nov 2016 06:25:53 +0530 Subject: [PATCH] Improve update of jsonb fields. Add PG 9.5 to travis. (#2984) * Improve update of jsonb fields. Add PG 9.5 to travis. * Replace manual escaping with pg-promise's built in --- .travis.yml | 4 +++- spec/RestCreate.spec.js | 2 +- .../Storage/Postgres/PostgresStorageAdapter.js | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06b1995f..96240807 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: node_js +dist: trusty +sudo: required node_js: - '4.5' - '6.1' @@ -6,7 +8,7 @@ services: - postgresql - redis-server addons: - postgresql: '9.4' + postgresql: '9.5' before_script: - ls -al "$HOME/.mongodb/versions" - psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index 89395231..ce553ea3 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -52,7 +52,7 @@ describe('rest create', () => { }); }); - it_exclude_dbs(['postgres'])('handles object and subdocument', done => { + it('handles object and subdocument', done => { let obj = { subdoc: {foo: 'bar', wu: 'tan'} }; rest.create(config, auth.nobody(config), 'MyClass', obj) .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index fcc9a4c7..5870e335 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -810,6 +810,7 @@ export class PostgresStorageAdapter { let index = 2; schema = toPostgresSchema(schema); + const originalUpdate = {...update}; update = handleDotFields(update); // Resolve authData first, // So we don't end up with multiple key updates @@ -914,9 +915,19 @@ export class PostgresStorageAdapter { } else if (typeof fieldValue === 'object' && schema.fields[fieldName] && schema.fields[fieldName].type === 'Object') { - updatePatterns.push(`$${index}:name = $${index + 1}`); - values.push(fieldName, fieldValue); - index += 2; + const keysToDelete = Object.keys(originalUpdate).filter(k => { + // choose top level fields that have a delete operation set + return originalUpdate[k].__op === 'Delete' && k.split('.').length === 2 + }).map(k => k.split('.')[1]); + + const deletePatterns = keysToDelete.reduce((p, c, i) => { + return p + ` - '$${index + 1 + i}:value'`; + }, ''); + + updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} || $${index + 1 + keysToDelete.length}::jsonb )`); + + values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue)); + index += 2 + keysToDelete.length; } else if (Array.isArray(fieldValue) && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') {