@platformatic/sql-mapper 0.7.0 → 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.
95
+ }
96
+
97
+ // insert
98
+ if (autoTimestamp && fields.inserted_at) {
99
+ /* istanbul ignore next */
100
+ now = now || new Date()
101
+ input.inserted_at = now
81
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
 
@@ -130,7 +153,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
130
153
  }
131
154
  const criteria = computeCriteria(args)
132
155
 
133
- const res = await queries.updateMany(db, sql, table, criteria, input, fieldsToRetrieve)
156
+ const res = await queries.updateMany(db, sql, table, schema, criteria, input, fieldsToRetrieve)
134
157
  return res.map(fixOutput)
135
158
  }
136
159
 
@@ -165,7 +188,8 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
165
188
  gt: '>',
166
189
  gte: '>=',
167
190
  lt: '<',
168
- lte: '<='
191
+ lte: '<=',
192
+ like: 'LIKE'
169
193
  }
170
194
 
171
195
  function computeCriteria (opts) {
@@ -186,6 +210,14 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
186
210
  criteria.push(sql`${sql.ident(field)} IS NULL`)
187
211
  } else if (operator === '<>' && value[key] === null) {
188
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]}`)
189
221
  } else {
190
222
  criteria.push(sql`${sql.ident(field)} ${sql.__dangerous__rawValue(operator)} ${computeCriteriaValue(fieldWrap, value[key])}`)
191
223
  }
@@ -215,9 +247,10 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
215
247
  const db = opts.tx || defaultDb
216
248
  const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
217
249
  const criteria = computeCriteria(opts)
250
+
218
251
  let query = sql`
219
252
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
220
- FROM ${sql.ident(table)}
253
+ FROM ${tableName(sql, table, schema)}
221
254
  `
222
255
 
223
256
  if (criteria.length > 0) {
@@ -250,7 +283,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
250
283
  let totalCountQuery = null
251
284
  totalCountQuery = sql`
252
285
  SELECT COUNT(*) AS total
253
- FROM ${sql.ident(table)}
286
+ FROM ${tableName(sql, table, schema)}
254
287
  `
255
288
  const criteria = computeCriteria(opts)
256
289
  if (criteria.length > 0) {
@@ -264,7 +297,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
264
297
  const db = opts.tx || defaultDb
265
298
  const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
266
299
  const criteria = computeCriteria(opts)
267
- const res = await queries.deleteAll(db, sql, table, criteria, fieldsToRetrieve)
300
+ const res = await queries.deleteAll(db, sql, table, schema, criteria, fieldsToRetrieve)
268
301
  return res.map(fixOutput)
269
302
  }
270
303
 
@@ -272,8 +305,9 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
272
305
  name: entityName,
273
306
  singularName: camelcase(singularize(table)),
274
307
  pluralName: camelcase(table),
275
- primaryKey,
308
+ primaryKeys,
276
309
  table,
310
+ schema,
277
311
  fields,
278
312
  camelCasedFields,
279
313
  fixInput,
@@ -287,9 +321,9 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKey, relations
287
321
  }
288
322
  }
289
323
 
290
- async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore) {
324
+ async function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, ignore) {
291
325
  // Compute the columns
292
- 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])
293
327
  const fields = columns.reduce((acc, column) => {
294
328
  acc[column.column_name] = {
295
329
  sqlType: column.udt_name,
@@ -307,10 +341,11 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
307
341
  }
308
342
  return acc
309
343
  }, {})
344
+
310
345
  // To get enum values in pg
311
346
  /* istanbul ignore next */
312
347
  if (db.isPg) {
313
- const enums = await queries.listEnumValues(db, sql, table)
348
+ const enums = await queries.listEnumValues(db, sql, table, schema)
314
349
  for (const enumValue of enums) {
315
350
  if (!fields[enumValue.column_name].enum) {
316
351
  fields[enumValue.column_name].enum = [enumValue.enumlabel]
@@ -321,8 +356,8 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
321
356
  }
322
357
  const currentRelations = []
323
358
 
324
- const constraintsList = await queries.listConstraints(db, sql, table)
325
- let primaryKey
359
+ const constraintsList = await queries.listConstraints(db, sql, table, schema)
360
+ const primaryKeys = new Set()
326
361
 
327
362
  for (const constraint of constraintsList) {
328
363
  const field = fields[constraint.column_name]
@@ -337,12 +372,12 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
337
372
  }
338
373
 
339
374
  if (constraint.constraint_type === 'PRIMARY KEY') {
340
- primaryKey = constraint.column_name
375
+ primaryKeys.add(constraint.column_name)
341
376
  // Check for SQLite typeless PK
342
377
  /* istanbul ignore next */
343
378
  if (db.isSQLite) {
344
379
  const validTypes = ['integer', 'uuid', 'serial']
345
- const pkType = fields[primaryKey].sqlType.toLowerCase()
380
+ const pkType = fields[constraint.column_name].sqlType.toLowerCase()
346
381
  if (!validTypes.includes(pkType)) {
347
382
  throw new Error(`Invalid Primary Key type. Expected "integer", found "${pkType}"`)
348
383
  }
@@ -356,7 +391,7 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, ignore)
356
391
  }
357
392
  }
358
393
 
359
- 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)
360
395
  entity.relations = currentRelations
361
396
 
362
397
  return entity
@@ -1,75 +1,98 @@
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`
24
+ async function listColumns (db, sql, table, schema) {
25
+ const query = sql`
14
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
 
57
- async function updateMany (db, sql, table, criteria, input, fieldsToRetrieve) {
76
+ async function updateMany (db, sql, table, schema, criteria, input, fieldsToRetrieve) {
58
77
  const pairs = Object.keys(input).map((key) => {
59
- const value = input[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
+ }
60
83
  return sql`${sql.ident(key)} = ${value}`
61
84
  })
62
85
 
63
86
  const selectIds = sql`
64
87
  SELECT id
65
- FROM ${sql.ident(table)}
88
+ FROM ${tableName(sql, table, schema)}
66
89
  WHERE ${sql.join(criteria, sql` AND `)}
67
90
  `
68
91
  const resp = await db.query(selectIds)
69
92
  const ids = resp.map(({ id }) => id)
70
93
 
71
94
  const update = sql`
72
- UPDATE ${sql.ident(table)}
95
+ UPDATE ${tableName(sql, table, schema)}
73
96
  SET ${sql.join(pairs, sql`, `)}
74
97
  WHERE ${sql.join(criteria, sql` AND `)}
75
98
  `
@@ -78,7 +101,7 @@ async function updateMany (db, sql, table, criteria, input, fieldsToRetrieve) {
78
101
 
79
102
  const select = sql`
80
103
  SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
81
- FROM ${sql.ident(table)}
104
+ FROM ${tableName(sql, table, schema)}
82
105
  WHERE id IN (${ids});
83
106
  `
84
107
  const res = await db.query(select)
@@ -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)
@@ -80,14 +95,16 @@ module.exports.updateOne = updateOne
80
95
 
81
96
  module.exports.updateMany = shared.updateMany
82
97
 
83
- async function listEnumValues (db, sql, table) {
84
- return (await db.query(sql`
85
- SELECT udt_name, enumlabel, column_name
86
- FROM pg_enum e
87
- JOIN pg_type t ON e.enumtypid = t.oid
88
- JOIN information_schema.columns c on c.udt_name = t.typname
89
- WHERE table_name = ${table};
90
- `))
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)
91
108
  }
92
109
 
93
110
  module.exports.listEnumValues = listEnumValues