From 9e99cf5820d2948a82417b133366d06780e229f1 Mon Sep 17 00:00:00 2001 From: Kulshekhar Kabra Date: Wed, 2 Nov 2016 05:09:14 +0530 Subject: [PATCH 1/2] Improve update of jsonb fields. Add PG 9.5 to travis. --- .travis.yml | 4 +++- spec/RestCreate.spec.js | 2 +- .../Storage/Postgres/PostgresStorageAdapter.js | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06b1995fe1..96240807de 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 893952313f..ce553ea369 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 fcc9a4c77f..6a5bf91e92 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,20 @@ 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]) + .map(k => k.replace(/'/g, `''`)); + + const deletePatterns = keysToDelete.reduce((p, c, i) => { + return p + ` - '$${index + 1 + i}:raw'`; + }, ''); + + 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') { From dc47daf6119d12d3785e116df8c9c41bacbd0057 Mon Sep 17 00:00:00 2001 From: Kulshekhar Kabra Date: Wed, 2 Nov 2016 05:48:04 +0530 Subject: [PATCH 2/2] Replace manual escaping with pg-promise's built in --- src/Adapters/Storage/Postgres/PostgresStorageAdapter.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 6a5bf91e92..5870e33502 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -918,11 +918,10 @@ export class PostgresStorageAdapter { 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]) - .map(k => k.replace(/'/g, `''`)); + }).map(k => k.split('.')[1]); const deletePatterns = keysToDelete.reduce((p, c, i) => { - return p + ` - '$${index + 1 + i}:raw'`; + return p + ` - '$${index + 1 + i}:value'`; }, ''); updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} || $${index + 1 + keysToDelete.length}::jsonb )`);