From 42a50ad0a8567c7d0639a529df336a59c815bcd5 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Wed, 30 Oct 2024 08:08:02 +0300 Subject: [PATCH] db-util: use sql.*() instead of slonik-sql-tag-raw (#1240) --- lib/model/query/submissions.js | 4 ++-- lib/util/db.js | 19 +++++++++---------- test/unit/util/db.js | 26 +++++++++++++------------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/model/query/submissions.js b/lib/model/query/submissions.js index f848498ce..ef9b93805 100644 --- a/lib/model/query/submissions.js +++ b/lib/model/query/submissions.js @@ -340,9 +340,9 @@ const _exportUnjoiner = unjoiner(Submission, Submission.Def, Submission.Xml, Sub // we just use the terrible hack. const { raw } = require('slonik-sql-tag-raw'); const _exportFields = raw(_exportUnjoiner.fields.sql - .replace(',submissions."userAgent" as "submissions!userAgent"', '') + .replace(',"submissions"."userAgent" as "submissions!userAgent"', '') .replace( - 'submission_defs."xml" as "submission_defs!xml"', + '"submission_defs"."xml" as "submission_defs!xml"', '(case when submission_defs."localKey" is null then submission_defs.xml end) as "submission_defs!xml"' )); diff --git a/lib/util/db.js b/lib/util/db.js index 43b459410..c88185d6b 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -10,7 +10,6 @@ const { inspect } = require('util'); const { mergeRight, pick, always, without, remove, indexOf } = require('ramda'); const { sql } = require('slonik'); -const { raw } = require('slonik-sql-tag-raw'); const { reject } = require('./promise'); const Problem = require('./problem'); const Option = require('./option'); @@ -87,9 +86,9 @@ const unjoiner = (...frames) => { const isOption = MaybeInstance instanceof Option; const Instance = isOption ? MaybeInstance.get() : MaybeInstance; for (const field of Instance.fields) { - const fullname = (Instance.from == null) ? `"${field}"` : `${Instance.from}."${field}"`; + const fullname = (Instance.from == null) ? sql.identifier([field]) : sql.identifier([Instance.from, field]); const sqlname = (Instance.from == null) ? field : `${Instance.from}!${field}`; - fields.push(`${fullname} as "${sqlname}"`); + fields.push(sql`${fullname} as ${sql.identifier([sqlname])}`); unmap[sqlname] = Instance.to; unprefix[sqlname] = field; constructors[Instance.to] = isOption ? maybeConstruct(Instance) : construct(Instance); @@ -112,7 +111,7 @@ const unjoiner = (...frames) => { return new frames[0](primary, bag); }; - unjoin.fields = raw(fields.join(',')); + unjoin.fields = sql`${sql.join(fields, sql`,`)}`; // FIXME remove wrapping sql`` return unjoin; }; @@ -147,7 +146,7 @@ const insert = (obj) => { if (obj.constructor.hasCreatedAt) keys.push('createdAt'); const fieldlist = sql.join(keys.map((k) => sql.identifier([ k ])), sql`,`); return sql` -insert into ${raw(obj.constructor.table)} (${fieldlist}) +insert into ${sql.identifier([obj.constructor.table])} (${fieldlist}) values (${sql.join(keys.map(_assign(obj)), sql`,`)}) returning *`; }; @@ -167,7 +166,7 @@ const insertMany = (objs) => { // we need to set clock_timestamp if there's createdAt column // Slonik doesn't support setting sql identitfier for sql.unnest yet if (Type.hasCreatedAt) { - columns = sql`"createdAt", ${raw(without(['createdAt'], Type.insertfields).map((s) => `"${s}"`).join(','))}`; + columns = sql`"createdAt", ${sql.join(without(['createdAt'], Type.insertfields).map(f => sql.identifier([f])), sql`,`)}`; rows = objs.map(obj => without(['createdAt'], Type.insertfields).map(_assign(obj))); columnTypes = remove(indexOf('createdAt', Type.insertfields), 1, Type.insertFieldTypes); selectExp = sql`clock_timestamp(), *`; @@ -179,7 +178,7 @@ const insertMany = (objs) => { } return sql` - INSERT INTO ${raw(Type.table)} (${columns}) + INSERT INTO ${sql.identifier([Type.table])} (${columns}) SELECT ${selectExp} FROM ${sql.unnest(rows, columnTypes)} AS t`; }; @@ -190,7 +189,7 @@ const updater = (obj, data, whereKey = 'id') => { if (keys.length === 0) return sql`select true`; const assigner = _assign(data); return sql` -update ${raw(obj.constructor.table)} +update ${sql.identifier([obj.constructor.table])} set ${sql.join(keys.map((k) => sql`${sql.identifier([ k ])}=${assigner(k)}`), sql`,`)} ${(obj.constructor.hasUpdatedAt && !keys.includes('updatedAt')) ? sql`,"updatedAt"=clock_timestamp()` : nothing} where ${sql.identifier([ whereKey ])}=${obj[whereKey]} @@ -199,10 +198,10 @@ returning *`; // generic del utility const markDeleted = (obj) => - sql`update ${raw(obj.constructor.table)} set "deletedAt"=now() where id=${obj.id}`; + sql`update ${sql.identifier([obj.constructor.table])} set "deletedAt"=now() where id=${obj.id}`; const markUndeleted = (obj) => - sql`update ${raw(obj.constructor.table)} set "deletedAt"=null where id=${obj.id}`; + sql`update ${sql.identifier([obj.constructor.table])} set "deletedAt"=null where id=${obj.id}`; //////////////////////////////////////// diff --git a/test/unit/util/db.js b/test/unit/util/db.js index 0dcf86df0..07ae9a791 100644 --- a/test/unit/util/db.js +++ b/test/unit/util/db.js @@ -206,7 +206,7 @@ describe('util/db', () => { const U = Frame.define(into('extra'), 'z'); it('should generate fields', () => { unjoiner(T, U) - .fields.should.eql(sql`frames."x" as "frames!x",frames."y" as "frames!y","z" as "z"`); + .fields.should.eql(sql`"frames"."x" as "frames!x","frames"."y" as "frames!y","z" as "z"`); }); it('should unjoin data', () => { @@ -219,7 +219,7 @@ describe('util/db', () => { it('should optionally unjoin optional data', () => { const unjoin = unjoiner(T, Option.of(U)); - unjoin.fields.should.eql(sql`frames."x" as "frames!x",frames."y" as "frames!y","z" as "z"`); + unjoin.fields.should.eql(sql`"frames"."x" as "frames!x","frames"."y" as "frames!y","z" as "z"`); unjoin({ 'frames!x': 3, 'frames!y': 4, z: 5 }) .should.eql(new T({ x: 3, y: 4 }, { extra: Option.of(new U({ z: 5 })) })); unjoin({ 'frames!x': 3, 'frames!y': 4 }) @@ -239,7 +239,7 @@ describe('util/db', () => { it('should provide the appropriate arguments when not extended', () => { let run = false; extender(T)(U)((fields, extend, options, x, y, z) => { - fields.should.eql(sql`frames."x" as "frames!x",frames."y" as "frames!y"`); + fields.should.eql(sql`"frames"."x" as "frames!x","frames"."y" as "frames!y"`); (sql`${extend|| true}`).should.eql(sql``); x.should.equal(2); y.should.equal(3); @@ -252,7 +252,7 @@ describe('util/db', () => { it('should provide the appropriate arguments when extended', () => { let run = false; extender(T)(U)((fields, extend, options, x, y, z) => { - fields.should.eql(sql`frames."x" as "frames!x",frames."y" as "frames!y","a" as "a","b" as "b"`); + fields.should.eql(sql`"frames"."x" as "frames!x","frames"."y" as "frames!y","a" as "a","b" as "b"`); (sql`${extend|| true}`).should.eql(sql`${true}`); x.should.equal(2); y.should.equal(3); @@ -285,7 +285,7 @@ describe('util/db', () => { it('should formulate a basic response based on data', () => { insert(new T({ x: 2, y: 3 })).should.eql(sql` -insert into frames ("x","y") +insert into "frames" ("x","y") values (${2},${3}) returning *`); }); @@ -293,7 +293,7 @@ returning *`); it('should deal with strange data input types', () => { insert(new T({ x: { test: true }, y: undefined, z: new Date('2000-01-01'), w: Object.assign(Object.create(null), { foo: 'bar' }) })) .should.eql(sql` -insert into frames ("x","y","z","w") +insert into "frames" ("x","y","z","w") values (${'{"test":true}'},${null},${'2000-01-01T00:00:00.000Z'},${'{"foo":"bar"}'}) returning *`); }); @@ -301,7 +301,7 @@ returning *`); it('should automatically insert into createdAt if expected', () => { const U = Frame.define(table('cats'), 'createdAt', 'updatedAt'); insert(new U()).should.eql(sql` -insert into cats ("createdAt") +insert into "cats" ("createdAt") values (${sql`clock_timestamp()`}) returning *`); }); @@ -318,7 +318,7 @@ returning *`); it('should insert all data', () => { const query = insertMany([ new T({ x: 2 }), new T({ y: 3 }) ]); query.sql.should.be.eql(` - INSERT INTO dogs ("x","y") + INSERT INTO "dogs" ("x","y") SELECT * FROM unnest($1::"text"[], $2::"text"[]) AS t`); query.values.should.be.eql([ [2, null], @@ -330,7 +330,7 @@ returning *`); const U = Frame.define(table('dogs'), 'x', 'createdAt', fieldTypes(['timestamptz', 'timestamptz'])); const query = insertMany([ new U({ x: new Date('2000-01-01') }), new U() ]); query.sql.should.be.eql(` - INSERT INTO dogs ("createdAt", "x") + INSERT INTO "dogs" ("createdAt", "x") SELECT clock_timestamp(), * FROM unnest($1::"timestamptz"[]) AS t`); query.values.should.be.eql([ ['2000-01-01T00:00:00.000Z', null] @@ -341,7 +341,7 @@ returning *`); const U = Frame.define(table('dogs'), 'x', 'createdAt', 'age', fieldTypes(['timestamptz', 'timestamptz', 'int4'])); const query = insertMany([ new U({ x: new Date('2000-01-01'), age: 14 }), new U({ age: 8 }), new U() ]); query.sql.should.be.eql(` - INSERT INTO dogs ("createdAt", "x","age") + INSERT INTO "dogs" ("createdAt", "x","age") SELECT clock_timestamp(), * FROM unnest($1::"timestamptz"[], $2::"int4"[]) AS t`); query.values.should.be.eql([ ['2000-01-01T00:00:00.000Z', null, null], @@ -362,7 +362,7 @@ returning *`); it('should update the given data', () => { updater(new T({ id: 1, x: 2 }), new T({ y: 3 })).should.eql(sql` -update rabbits +update "rabbits" set "y"=${3} where ${sql.identifier([ 'id' ])}=${1} @@ -372,7 +372,7 @@ returning *`); it('should set updatedAt if present', () => { const U = Frame.define(table('rabbits'), 'createdAt', 'updatedAt'); updater(new U({ id: 1, x: 2 }), new U({ y: 3 })).should.eql(sql` -update rabbits +update "rabbits" set "y"=${3} ,"updatedAt"=clock_timestamp() where "id"=${1} @@ -381,7 +381,7 @@ returning *`); it('should use a different id key if given', () => { updater(new T({ otherId: 0, x: 2 }), new T({ y: 3 }), 'otherId').should.eql(sql` -update rabbits +update "rabbits" set "y"=${3} where "otherId"=${0}