@platformatic/sql-mapper 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/entity.js CHANGED
@@ -3,10 +3,11 @@
3
3
  const camelcase = require('camelcase')
4
4
  const { singularize } = require('inflected')
5
5
  const {
6
- toSingular
6
+ toSingular,
7
+ tableName
7
8
  } = require('./utils')
8
9
 
9
- function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations, queries, autoTimestamp) {
10
+ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relations, queries, autoTimestamp, schema) {
10
11
  const entityName = toSingular(table)
11
12
 
12
13
  // Fields remapping
@@ -21,6 +22,13 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
21
22
  return acc
22
23
  }, {})
23
24
 
25
+ const primaryKeysTypes = Array.from(primaryKeys).map((key) => {
26
+ return {
27
+ key,
28
+ sqlType: fields[key].sqlType
29
+ }
30
+ })
31
+
24
32
  function fixInput (input) {
25
33
  const newInput = {}
26
34
  for (const key of Object.keys(input)) {
@@ -46,7 +54,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
46
54
  for (const key of Object.keys(output)) {
47
55
  let value = output[key]
48
56
  const newKey = fieldMapToRetrieve[key]
49
- if (key === primaryKey && value !== null && value !== undefined) {
57
+ if (primaryKeys.has(key) && value !== null && value !== undefined) {
50
58
  value = value.toString()
51
59
  }
52
60
  newOutput[newKey] = value
@@ -62,23 +70,38 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
62
70
  // args.input is not array
63
71
  const fieldsToRetrieve = computeFields(args.fields).map((f) => sql.ident(f))
64
72
  const input = fixInput(args.input)
73
+
74
+ let hasPrimaryKeys = true
75
+ for (const key of primaryKeys) {
76
+ if (input[key] === undefined) {
77
+ hasPrimaryKeys = false
78
+ break
79
+ }
80
+ }
81
+
65
82
  let now
66
83
  if (autoTimestamp && fields.updated_at) {
67
84
  now = new Date()
68
85
  input.updated_at = now
69
86
  }
70
- if (input[primaryKey]) { // update
71
- const res = await queries.updateOne(db, sql, table, input, primaryKey, fieldsToRetrieve)
72
- return fixOutput(res)
73
- } else { // insert
74
- if (autoTimestamp && fields.inserted_at) {
75
- /* istanbul ignore next */
76
- now = now || new Date()
77
- input.inserted_at = now
87
+ if (hasPrimaryKeys) { // update
88
+ const res = await queries.updateOne(db, sql, table, schema, input, primaryKeys, fieldsToRetrieve)
89
+ if (res) {
90
+ return fixOutput(res)
78
91
  }
79
- const res = await queries.insertOne(db, sql, table, input, primaryKey, fields[primaryKey].sqlType.toLowerCase() === 'uuid', fieldsToRetrieve)
80
- return fixOutput(res)
92
+ // If we are here, the record does not exist, so we create it
93
+ // this is inefficient because it will do 2 queries.
94
+ // TODO there is a way to do it in one query with DB specific syntax.
81
95
  }
96
+
97
+ // insert
98
+ if (autoTimestamp && fields.inserted_at) {
99
+ /* istanbul ignore next */
100
+ now = now || new Date()
101
+ input.inserted_at = now
102
+ }
103
+ const res = await queries.insertOne(db, sql, table, schema, input, primaryKeysTypes, fieldsToRetrieve)
104
+ return fixOutput(res)
82
105
  }
83
106
 
84
107
  async function insert (args) {
@@ -101,14 +124,14 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
101
124
  /* istanbul ignore next */
102
125
  if (queries.insertMany) {
103
126
  // We are not fixing the input here because it is done in the query.
104
- const res = await queries.insertMany(db, sql, table, inputs, inputToFieldMap, primaryKey, fieldsToRetrieve, fields)
127
+ const res = await queries.insertMany(db, sql, table, schema, inputs, inputToFieldMap, primaryKeysTypes, fieldsToRetrieve, fields)
105
128
  return res.map(fixOutput)
106
129
  } else {
107
130
  // TODO this can be optimized, we can still use a batch insert if we do not want any fields
108
131
  const res = []
109
132
  for (let input of inputs) {
110
133
  input = fixInput(input)
111
- const resOne = await queries.insertOne(db, sql, table, input, primaryKey, fields[primaryKey].sqlType.toLowerCase() === 'uuid', fieldsToRetrieve)
134
+ const resOne = await queries.insertOne(db, sql, table, schema, input, primaryKeysTypes, fieldsToRetrieve)
112
135
  res.push(fixOutput(resOne))
113
136
  }
114
137
 
@@ -116,6 +139,24 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
116
139
  }
117
140
  }
118
141
 
142
+ async function updateMany (args) {
143
+ const db = args.tx || defaultDb
144
+ const fieldsToRetrieve = computeFields(args.fields).map((f) => sql.ident(f))
145
+ if (args.input === undefined) {
146
+ throw new Error('Input not provided.')
147
+ }
148
+ const input = fixInput(args.input)
149
+ let now
150
+ if (autoTimestamp && fields.updated_at) {
151
+ now = new Date()
152
+ input.updated_at = now
153
+ }
154
+ const criteria = computeCriteria(args)
155
+
156
+ const res = await queries.updateMany(db, sql, table, schema, criteria, input, fieldsToRetrieve)
157
+ return res.map(fixOutput)
158
+ }
159
+
119
160
  function computeFields (fields) {
120
161
  if (!fields) {
121
162
  return Object.values(inputToFieldMap)
@@ -147,7 +188,8 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
147
188
  gt: '>',
148
189
  gte: '>=',
149
190
  lt: '<',
150
- lte: '<='
191
+ lte: '<=',
192
+ like: 'LIKE'
151
193
  }
152
194
 
153
195
  function computeCriteria (opts) {
@@ -168,6 +210,14 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
168
210
  criteria.push(sql`${sql.ident(field)} IS NULL`)
169
211
  } else if (operator === '<>' && value[key] === null) {
170
212
  criteria.push(sql`${sql.ident(field)} IS NOT NULL`)
213
+ } else if (operator === 'LIKE') {
214
+ let leftHand = sql.ident(field)
215
+ // NOTE: cast fields AS CHAR(64) and TRIM the whitespaces
216
+ // to prevent errors with fields different than VARCHAR & TEXT
217
+ if (!['text', 'varchar'].includes(fieldWrap.sqlType)) {
218
+ leftHand = sql`TRIM(CAST(${sql.ident(field)} AS CHAR(64)))`
219
+ }
220
+ criteria.push(sql`${leftHand} LIKE ${value[key]}`)
171
221
  } else {
172
222
  criteria.push(sql`${sql.ident(field)} ${sql.__dangerous__rawValue(operator)} ${computeCriteriaValue(fieldWrap, value[key])}`)
173
223
  }
@@ -197,9 +247,10 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
197
247
  const db = opts.tx || defaultDb
198
248
  const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
199
249
  const criteria = computeCriteria(opts)
250
+
200
251
  let query = sql`
201
252
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
202
- FROM ${sql.ident(table)}
253
+ FROM ${tableName(sql, table, schema)}
203
254
  `
204
255
 
205
256
  if (criteria.length > 0) {
@@ -232,7 +283,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
232
283
  let totalCountQuery = null
233
284
  totalCountQuery = sql`
234
285
  SELECT COUNT(*) AS total
235
- FROM ${sql.ident(table)}
286
+ FROM ${tableName(sql, table, schema)}
236
287
  `
237
288
  const criteria = computeCriteria(opts)
238
289
  if (criteria.length > 0) {
@@ -246,7 +297,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
246
297
  const db = opts.tx || defaultDb
247
298
  const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
248
299
  const criteria = computeCriteria(opts)
249
- const res = await queries.deleteAll(db, sql, table, criteria, fieldsToRetrieve)
300
+ const res = await queries.deleteAll(db, sql, table, schema, criteria, fieldsToRetrieve)
250
301
  return res.map(fixOutput)
251
302
  }
252
303
 
@@ -254,8 +305,9 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
254
305
  name: entityName,
255
306
  singularName: camelcase(singularize(table)),
256
307
  pluralName: camelcase(table),
257
- primaryKey,
308
+ primaryKeys,
258
309
  table,
310
+ schema,
259
311
  fields,
260
312
  camelCasedFields,
261
313
  fixInput,
@@ -264,28 +316,48 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
264
316
  count,
265
317
  insert,
266
318
  save,
267
- delete: _delete
319
+ delete: _delete,
320
+ updateMany
268
321
  }
269
322
  }
270
323
 
271
- async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore) {
324
+ async function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, ignore) {
272
325
  // Compute the columns
273
- const columns = (await queries.listColumns(db, sql, table)).filter((c) => !ignore[c.column_name])
326
+ const columns = (await queries.listColumns(db, sql, table, schema)).filter((c) => !ignore[c.column_name])
274
327
  const fields = columns.reduce((acc, column) => {
275
328
  acc[column.column_name] = {
276
329
  sqlType: column.udt_name,
277
330
  isNullable: column.is_nullable === 'YES'
278
331
  }
332
+
333
+ // To get enum values in mysql and mariadb
334
+ /* istanbul ignore next */
335
+ if (column.udt_name === 'enum') {
336
+ acc[column.column_name].enum = column.column_type.match(/'(.+?)'/g).map(enumValue => enumValue.slice(1, enumValue.length - 1))
337
+ }
338
+
279
339
  if (autoTimestamp && (column.column_name === 'updated_at' || column.column_name === 'inserted_at')) {
280
340
  acc[column.column_name].autoTimestamp = true
281
341
  }
282
342
  return acc
283
343
  }, {})
284
344
 
345
+ // To get enum values in pg
346
+ /* istanbul ignore next */
347
+ if (db.isPg) {
348
+ const enums = await queries.listEnumValues(db, sql, table, schema)
349
+ for (const enumValue of enums) {
350
+ if (!fields[enumValue.column_name].enum) {
351
+ fields[enumValue.column_name].enum = [enumValue.enumlabel]
352
+ } else {
353
+ fields[enumValue.column_name].enum.push(enumValue.enumlabel)
354
+ }
355
+ }
356
+ }
285
357
  const currentRelations = []
286
358
 
287
- const constraintsList = await queries.listConstraints(db, sql, table)
288
- let primaryKey
359
+ const constraintsList = await queries.listConstraints(db, sql, table, schema)
360
+ const primaryKeys = new Set()
289
361
 
290
362
  for (const constraint of constraintsList) {
291
363
  const field = fields[constraint.column_name]
@@ -300,12 +372,12 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
300
372
  }
301
373
 
302
374
  if (constraint.constraint_type === 'PRIMARY KEY') {
303
- primaryKey = constraint.column_name
375
+ primaryKeys.add(constraint.column_name)
304
376
  // Check for SQLite typeless PK
305
377
  /* istanbul ignore next */
306
378
  if (db.isSQLite) {
307
379
  const validTypes = ['integer', 'uuid', 'serial']
308
- const pkType = fields[primaryKey].sqlType.toLowerCase()
380
+ const pkType = fields[constraint.column_name].sqlType.toLowerCase()
309
381
  if (!validTypes.includes(pkType)) {
310
382
  throw new Error(`Invalid Primary Key type. Expected "integer", found "${pkType}"`)
311
383
  }
@@ -319,7 +391,7 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
319
391
  }
320
392
  }
321
393
 
322
- const entity = createMapper(db, sql, log, table, fields, primaryKey, currentRelations, queries, autoTimestamp)
394
+ const entity = createMapper(db, sql, log, table, fields, primaryKeys, currentRelations, queries, autoTimestamp, schema)
323
395
  entity.relations = currentRelations
324
396
 
325
397
  return entity
@@ -1,62 +1,117 @@
1
1
  'use strict'
2
2
 
3
- async function listTables (db, sql) {
4
- const res = await db.query(sql`
5
- SELECT TABLE_NAME
6
- FROM information_schema.tables
7
- WHERE table_schema = (SELECT DATABASE())
8
- `)
9
- return res.map(r => r.TABLE_NAME)
3
+ const { tableName } = require('../utils')
4
+
5
+ async function listTables (db, sql, schemas) {
6
+ if (schemas) {
7
+ const schemaList = sql.__dangerous__rawValue(schemas.map(s => `'${s}'`))
8
+ const res = await db.query(sql`
9
+ SELECT TABLE_SCHEMA, TABLE_NAME
10
+ FROM information_schema.tables
11
+ WHERE table_schema in (${schemaList})
12
+ `)
13
+ return res.map(r => ({ schema: r.TABLE_SCHEMA, table: r.TABLE_NAME }))
14
+ } else {
15
+ const res = await db.query(sql`
16
+ SELECT TABLE_SCHEMA, TABLE_NAME
17
+ FROM information_schema.tables
18
+ WHERE table_schema = (SELECT DATABASE())
19
+ `)
20
+ return res.map(r => ({ schema: r.TABLE_SCHEMA, table: r.TABLE_NAME }))
21
+ }
10
22
  }
11
23
 
12
- async function listColumns (db, sql, table) {
13
- const res = await db.query(sql`
14
- SELECT column_name as column_name, data_type as udt_name, is_nullable as is_nullable
24
+ async function listColumns (db, sql, table, schema) {
25
+ const query = sql`
26
+ SELECT column_name as column_name, data_type as udt_name, is_nullable as is_nullable, column_type as column_type
15
27
  FROM information_schema.columns
16
28
  WHERE table_name = ${table}
17
- AND table_schema = (SELECT DATABASE())
18
- `)
19
- return res
29
+ AND table_schema = ${schema}
30
+ `
31
+ return db.query(query)
20
32
  }
21
33
 
22
- async function listConstraints (db, sql, table) {
23
- const res = await db.query(sql`
34
+ async function listConstraints (db, sql, table, schema) {
35
+ const query = sql`
24
36
  SELECT TABLE_NAME as table_name, COLUMN_NAME as column_name, CONSTRAINT_TYPE as constraint_type, referenced_table_name AS foreign_table_name, referenced_column_name AS foreign_column_name
25
37
  FROM information_schema.table_constraints t
26
38
  JOIN information_schema.key_column_usage k
27
39
  USING (constraint_name, table_schema, table_name)
28
40
  WHERE t.table_name = ${table}
29
- AND t.table_schema = (SELECT DATABASE())
30
- `)
31
-
32
- return res
41
+ AND t.table_schema = ${schema}
42
+ `
43
+ return db.query(query)
33
44
  }
34
45
 
35
- async function updateOne (db, sql, table, input, primaryKey, fieldsToRetrieve) {
46
+ async function updateOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
36
47
  const pairs = Object.keys(input).map((key) => {
37
- const value = input[key]
48
+ let value = input[key]
49
+ /* istanbul ignore next */
50
+ if (value && typeof value === 'object' && !(value instanceof Date)) {
51
+ value = JSON.stringify(value)
52
+ }
38
53
  return sql`${sql.ident(key)} = ${value}`
39
54
  })
55
+ const where = []
56
+ for (const key of primaryKeys) {
57
+ where.push(sql`${sql.ident(key)} = ${input[key]}`)
58
+ }
40
59
  const update = sql`
41
- UPDATE ${sql.ident(table)}
60
+ UPDATE ${tableName(sql, table, schema)}
42
61
  SET ${sql.join(pairs, sql`, `)}
43
- WHERE ${sql.ident(primaryKey)} = ${sql.value(input[primaryKey])}
62
+ WHERE ${sql.join(where, sql` AND `)}
44
63
  `
45
64
  await db.query(update)
46
65
 
47
66
  const select = sql`
48
67
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
49
- FROM ${sql.ident(table)}
50
- WHERE ${sql.ident(primaryKey)} = ${sql.value(input[primaryKey])}
68
+ FROM ${tableName(sql, table, schema)}
69
+ WHERE ${sql.join(where, sql` AND `)}
51
70
  `
52
71
 
53
72
  const res = await db.query(select)
54
73
  return res[0]
55
74
  }
56
75
 
76
+ async function updateMany (db, sql, table, schema, criteria, input, fieldsToRetrieve) {
77
+ const pairs = Object.keys(input).map((key) => {
78
+ let value = input[key]
79
+ /* istanbul ignore next */
80
+ if (value && typeof value === 'object' && !(value instanceof Date)) {
81
+ value = JSON.stringify(value)
82
+ }
83
+ return sql`${sql.ident(key)} = ${value}`
84
+ })
85
+
86
+ const selectIds = sql`
87
+ SELECT id
88
+ FROM ${tableName(sql, table, schema)}
89
+ WHERE ${sql.join(criteria, sql` AND `)}
90
+ `
91
+ const resp = await db.query(selectIds)
92
+ const ids = resp.map(({ id }) => id)
93
+
94
+ const update = sql`
95
+ UPDATE ${tableName(sql, table, schema)}
96
+ SET ${sql.join(pairs, sql`, `)}
97
+ WHERE ${sql.join(criteria, sql` AND `)}
98
+ `
99
+
100
+ await db.query(update)
101
+
102
+ const select = sql`
103
+ SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
104
+ FROM ${tableName(sql, table, schema)}
105
+ WHERE id IN (${ids});
106
+ `
107
+ const res = await db.query(select)
108
+ return res
109
+ }
110
+
57
111
  module.exports = {
58
112
  listTables,
59
113
  listColumns,
60
114
  listConstraints,
61
- updateOne
115
+ updateOne,
116
+ updateMany
62
117
  }
@@ -2,8 +2,9 @@
2
2
 
3
3
  const { insertPrep } = require('./shared')
4
4
  const shared = require('./mysql-shared')
5
+ const { tableName } = require('../utils')
5
6
 
6
- function insertOne (db, sql, table, input, primaryKey, useUUID, fieldsToRetrieve) {
7
+ function insertOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
7
8
  const keysToSql = Object.keys(input).map((key) => sql.ident(key))
8
9
  const keys = sql.join(
9
10
  keysToSql,
@@ -11,6 +12,11 @@ function insertOne (db, sql, table, input, primaryKey, useUUID, fieldsToRetrieve
11
12
  )
12
13
 
13
14
  const valuesToSql = Object.keys(input).map((key) => {
15
+ /* istanbul ignore next */
16
+ if (input[key] && typeof input[key] === 'object' && !(input[key] instanceof Date)) {
17
+ // This is a JSON field
18
+ return sql.value(JSON.stringify(input[key]))
19
+ }
14
20
  return sql.value(input[key])
15
21
  })
16
22
  const values = sql.join(
@@ -18,55 +24,97 @@ function insertOne (db, sql, table, input, primaryKey, useUUID, fieldsToRetrieve
18
24
  sql`, `
19
25
  )
20
26
 
21
- return db.tx(async function (db) {
22
- const insert = sql`
23
- INSERT INTO ${sql.ident(table)} (${keys})
27
+ if (primaryKeys.length === 1 && input[primaryKeys[0].key] === undefined) {
28
+ return db.tx(async function (db) {
29
+ const insert = sql`
30
+ INSERT INTO ${tableName(sql, table, schema)} (${keys})
24
31
  VALUES(${values})
25
32
  `
26
- await db.query(insert)
33
+ await db.query(insert)
27
34
 
28
- const res2 = await db.query(sql`
35
+ const res2 = await db.query(sql`
29
36
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
30
- FROM ${sql.ident(table)}
31
- WHERE ${sql.ident(primaryKey)} = (
37
+ FROM ${tableName(sql, table, schema)}
38
+ WHERE ${sql.ident(primaryKeys[0].key)} = (
32
39
  SELECT last_insert_id()
33
40
  )
34
41
  `)
35
42
 
36
- return res2[0]
37
- })
43
+ return res2[0]
44
+ })
45
+ } else {
46
+ const where = []
47
+ for (const { key } of primaryKeys) {
48
+ // TODO write a test that cover this
49
+ /* istanbul ignore next */
50
+ if (!input[key]) {
51
+ throw new Error(`Missing value for primary key ${key}`)
52
+ }
53
+ where.push(sql`${sql.ident(key)} = ${input[key]}`)
54
+ }
55
+
56
+ return db.tx(async function (db) {
57
+ const insert = sql`
58
+ INSERT INTO ${tableName(sql, table, schema)} (${keys})
59
+ VALUES(${values})
60
+ `
61
+ await db.query(insert)
62
+
63
+ const res2 = await db.query(sql`
64
+ SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
65
+ FROM ${tableName(sql, table, schema)}
66
+ WHERE ${sql.join(where, sql` AND `)}
67
+ `)
68
+
69
+ return res2[0]
70
+ })
71
+ }
38
72
  }
39
73
 
40
- function insertMany (db, sql, table, inputs, inputToFieldMap, primaryKey, fieldsToRetrieve, fields) {
74
+ function insertMany (db, sql, table, schema, inputs, inputToFieldMap, primaryKeys, fieldsToRetrieve, fields) {
41
75
  return db.tx(async function (db) {
42
76
  const { keys, values } = insertPrep(inputs, inputToFieldMap, fields, sql)
43
77
  const insert = sql`
44
- insert into ${sql.ident(table)} (${keys})
78
+ insert into ${tableName(sql, table, schema)} (${keys})
45
79
  values ${sql.join(values, sql`, `)}
46
80
  `
47
81
 
48
82
  await db.query(insert)
49
83
 
84
+ const orderBy = []
85
+ for (const { key } of primaryKeys) {
86
+ orderBy.push(sql`${sql.ident(key)} DESC`)
87
+ }
88
+
50
89
  const res = await db.query(sql`
51
90
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
52
- FROM ${sql.ident(table)}
53
- ORDER BY ${sql.ident(primaryKey)} DESC
91
+ FROM ${tableName(sql, table, schema)}
92
+ ORDER BY ${sql.join(orderBy, sql`, `)}
54
93
  LIMIT ${inputs.length}
55
94
  `)
56
95
 
57
96
  // To make consistent with shared.insertMany
58
97
  res.sort(function (a, b) {
59
- return a.id - b.id
98
+ let val = 0
99
+ for (const { key } of primaryKeys) {
100
+ val = a[key] - b[key]
101
+ if (val !== 0) {
102
+ return val
103
+ }
104
+ }
105
+ // The following should never happen
106
+ /* istanbul ignore next */
107
+ return val
60
108
  })
61
109
  return res
62
110
  })
63
111
  }
64
112
 
65
- function deleteAll (db, sql, table, criteria, fieldsToRetrieve) {
113
+ function deleteAll (db, sql, table, schema, criteria, fieldsToRetrieve) {
66
114
  return db.tx(async function (db) {
67
115
  let selectQuery = sql`
68
116
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
69
- FROM ${sql.ident(table)}
117
+ FROM ${tableName(sql, table, schema)}
70
118
  `
71
119
  /* istanbul ignore else */
72
120
  if (criteria.length > 0) {
@@ -79,7 +127,7 @@ function deleteAll (db, sql, table, criteria, fieldsToRetrieve) {
79
127
  const res = await db.query(selectQuery)
80
128
 
81
129
  let deleteQuery = sql`
82
- DELETE FROM ${sql.ident(table)}
130
+ DELETE FROM ${tableName(sql, table, schema)}
83
131
  `
84
132
 
85
133
  /* istanbul ignore else */
package/lib/queries/pg.js CHANGED
@@ -1,12 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const shared = require('./shared')
4
+ const { tableName } = require('../utils')
4
5
 
5
- async function insertOne (db, sql, table, input, primaryKey, isUuid, fieldsToRetrieve) {
6
+ async function insertOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
6
7
  const inputKeys = Object.keys(input)
7
8
  if (inputKeys.length === 0) {
8
9
  const insert = sql`
9
- INSERT INTO ${sql.ident(table)}
10
+ INSERT INTO ${tableName(sql, table, schema)}
10
11
  DEFAULT VALUES
11
12
  RETURNING ${sql.join(fieldsToRetrieve, sql`, `)}
12
13
  `
@@ -14,36 +15,46 @@ async function insertOne (db, sql, table, input, primaryKey, isUuid, fieldsToRet
14
15
  return res[0]
15
16
  }
16
17
 
17
- return shared.insertOne(db, sql, table, input, primaryKey, isUuid, fieldsToRetrieve)
18
+ return shared.insertOne(db, sql, table, schema, input, primaryKeys, fieldsToRetrieve)
18
19
  }
19
20
 
20
21
  module.exports.insertOne = insertOne
21
22
  module.exports.deleteAll = shared.deleteAll
22
23
  module.exports.insertMany = shared.insertMany
23
24
 
24
- async function listTables (db, sql) {
25
- return (await db.query(sql`
26
- SELECT tablename
25
+ async function listTables (db, sql, schemas) {
26
+ if (schemas) {
27
+ const schemaList = sql.__dangerous__rawValue(schemas.map(s => `'${s}'`))
28
+ const res = await db.query(sql`
29
+ SELECT tablename, schemaname
30
+ FROM pg_catalog.pg_tables
31
+ WHERE
32
+ schemaname in (${schemaList})`)
33
+ return res.map(r => ({ schema: r.schemaname, table: r.tablename }))
34
+ }
35
+ const res = await db.query(sql`
36
+ SELECT tablename, schemaname
27
37
  FROM pg_catalog.pg_tables
28
38
  WHERE
29
39
  schemaname = current_schema()
30
- `)).map(t => t.tablename)
40
+ `)
41
+ return res.map(r => ({ schema: r.schemaname, table: r.tablename }))
31
42
  }
32
43
 
33
44
  module.exports.listTables = listTables
34
45
 
35
- async function listColumns (db, sql, table) {
46
+ async function listColumns (db, sql, table, schema) {
36
47
  return db.query(sql`
37
48
  SELECT column_name, udt_name, is_nullable
38
49
  FROM information_schema.columns
39
50
  WHERE table_name = ${table}
40
- AND table_schema = current_schema()
51
+ AND table_schema = ${schema}
41
52
  `)
42
53
  }
43
54
 
44
55
  module.exports.listColumns = listColumns
45
56
 
46
- async function listConstraints (db, sql, table) {
57
+ async function listConstraints (db, sql, table, schema) {
47
58
  const query = sql`
48
59
  SELECT constraints.*, usage.*, usage2.table_name AS foreign_table_name, usage2.column_name AS foreign_column_name
49
60
  FROM information_schema.table_constraints constraints
@@ -52,24 +63,28 @@ async function listConstraints (db, sql, table) {
52
63
  AND constraints.table_name = ${table}
53
64
  JOIN information_schema.constraint_column_usage usage2
54
65
  ON usage.constraint_name = usage2.constraint_name
55
- AND usage.table_name = ${table}
66
+ AND ( usage.table_name = ${table}
67
+ AND usage.table_schema = ${schema} )
56
68
  `
57
-
58
69
  const constraintsList = await db.query(query)
59
70
  return constraintsList
60
71
  }
61
72
 
62
73
  module.exports.listConstraints = listConstraints
63
74
 
64
- async function updateOne (db, sql, table, input, primaryKey, fieldsToRetrieve) {
75
+ async function updateOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
65
76
  const pairs = Object.keys(input).map((key) => {
66
77
  const value = input[key]
67
78
  return sql`${sql.ident(key)} = ${value}`
68
79
  })
80
+ const where = []
81
+ for (const key of primaryKeys) {
82
+ where.push(sql`${sql.ident(key)} = ${input[key]}`)
83
+ }
69
84
  const update = sql`
70
- UPDATE ${sql.ident(table)}
85
+ UPDATE ${tableName(sql, table, schema)}
71
86
  SET ${sql.join(pairs, sql`, `)}
72
- WHERE ${sql.ident(primaryKey)} = ${sql.value(input[primaryKey])}
87
+ WHERE ${sql.join(where, sql` AND `)}
73
88
  RETURNING ${sql.join(fieldsToRetrieve, sql`, `)}
74
89
  `
75
90
  const res = await db.query(update)
@@ -77,3 +92,19 @@ async function updateOne (db, sql, table, input, primaryKey, fieldsToRetrieve) {
77
92
  }
78
93
 
79
94
  module.exports.updateOne = updateOne
95
+
96
+ module.exports.updateMany = shared.updateMany
97
+
98
+ async function listEnumValues (db, sql, table, schema) {
99
+ const query = sql`
100
+ SELECT udt_name, enumlabel, column_name
101
+ FROM pg_enum e
102
+ JOIN pg_type t ON e.enumtypid = t.oid
103
+ JOIN information_schema.columns c on c.udt_name = t.typname
104
+ WHERE table_name = ${table}
105
+ AND table_schema = ${schema};
106
+ `
107
+ return db.query(query)
108
+ }
109
+
110
+ module.exports.listEnumValues = listEnumValues