@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 +64 -29
- package/lib/queries/mysql-shared.js +52 -29
- package/lib/queries/mysql.js +66 -18
- package/lib/queries/pg.js +40 -23
- package/lib/queries/shared.js +17 -9
- package/lib/queries/sqlite.js +48 -29
- package/lib/utils.js +7 -1
- package/mapper.d.ts +4 -0
- package/mapper.js +23 -10
- package/package.json +1 -1
- package/test/composite.test.js +162 -0
- package/test/entity.test.js +83 -4
- package/test/entity_transaction.test.js +0 -1
- package/test/helper.js +21 -0
- package/test/mapper.test.js +10 -86
- package/test/schema.test.js +261 -0
- package/test/where.test.js +140 -0
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,
|
|
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
|
|
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 (
|
|
71
|
-
const res = await queries.updateOne(db, sql, table, input,
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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[
|
|
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,
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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 =
|
|
18
|
-
`
|
|
19
|
-
return
|
|
29
|
+
AND table_schema = ${schema}
|
|
30
|
+
`
|
|
31
|
+
return db.query(query)
|
|
20
32
|
}
|
|
21
33
|
|
|
22
|
-
async function listConstraints (db, sql, table) {
|
|
23
|
-
const
|
|
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 =
|
|
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,
|
|
46
|
+
async function updateOne (db, sql, table, schema, input, primaryKeys, fieldsToRetrieve) {
|
|
36
47
|
const pairs = Object.keys(input).map((key) => {
|
|
37
|
-
|
|
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
|
|
60
|
+
UPDATE ${tableName(sql, table, schema)}
|
|
42
61
|
SET ${sql.join(pairs, sql`, `)}
|
|
43
|
-
WHERE ${sql.
|
|
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
|
|
50
|
-
WHERE ${sql.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
104
|
+
FROM ${tableName(sql, table, schema)}
|
|
82
105
|
WHERE id IN (${ids});
|
|
83
106
|
`
|
|
84
107
|
const res = await db.query(select)
|
package/lib/queries/mysql.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
33
|
+
await db.query(insert)
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
const res2 = await db.query(sql`
|
|
29
36
|
SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
|
|
30
|
-
FROM ${sql
|
|
31
|
-
WHERE ${sql.ident(
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
53
|
-
ORDER BY ${sql.
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
85
|
+
UPDATE ${tableName(sql, table, schema)}
|
|
71
86
|
SET ${sql.join(pairs, sql`, `)}
|
|
72
|
-
WHERE ${sql.
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|