doix-db 1.0.18 → 1.0.20
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/README.md +8 -1
- package/index.js +2 -2
- package/lib/DbCall.js +255 -0
- package/lib/DbCallTracker.js +70 -0
- package/lib/DbClient.js +99 -65
- package/lib/DbLang.js +24 -31
- package/lib/DbPool.js +2 -2
- package/package.json +2 -2
- package/lib/DbEventLogger.js +0 -101
package/README.md
CHANGED
|
@@ -2,4 +2,11 @@
|
|
|
2
2
|

|
|
3
3
|
|
|
4
4
|
# node-doix-db
|
|
5
|
-
|
|
5
|
+
`doix-db` is a plug in for [doix](https://github.com/do-/node-doix) framework implementing a common interface to relational databases. It features:
|
|
6
|
+
* [DbClient](DbClient) — the database API available to `doix` [Job](https://github.com/do-/node-doix/wiki/Job)s;
|
|
7
|
+
* [DbModel](DbModel) — the set of classes representing the database structure;
|
|
8
|
+
* [DbQuery](DbQuery) — a `DbModel` based `SELECT` builder;
|
|
9
|
+
* [DbMigrationPlan](DbMigrationPlan) — a diff/patch tool to compare existing database structure with the required `DbModel`;
|
|
10
|
+
* [DbLang](DbLang) — a set of SQL generating functions for miscellaneous application tasks.
|
|
11
|
+
|
|
12
|
+
* More information is available at https://github.com/do-/node-doix-db/wiki
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
DbClient: require ('./lib/DbClient.js'),
|
|
3
|
+
DbCallTracker : require ('./lib/DbCallTracker.js'),
|
|
4
|
+
DbClient: require ('./lib/DbClient.js'),
|
|
5
5
|
DbPool: require ('./lib/DbPool.js'),
|
|
6
6
|
DbLang: require ('./lib/DbLang.js'),
|
|
7
7
|
DbColumn: require ('./lib/model/DbColumn.js'),
|
package/lib/DbCall.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
const EV_FINISH = 'finish'
|
|
2
|
+
const EV_ERROR = 'error'
|
|
3
|
+
const EV_END_STREAM = [
|
|
4
|
+
'end',
|
|
5
|
+
'close',
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const OPT_REQUIRING_MAX_ROW = [
|
|
9
|
+
'rowMode',
|
|
10
|
+
'checkOverflow',
|
|
11
|
+
'minRows',
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const NULL = Symbol.for ('NULL'), mayBeNull = v => v === NULL ? null : v
|
|
15
|
+
|
|
16
|
+
const MD_ARRAY = 'array'
|
|
17
|
+
const MD_OBJECT = 'object'
|
|
18
|
+
const MD_SCALAR = 'scalar'
|
|
19
|
+
|
|
20
|
+
const EventEmitter = require ('events')
|
|
21
|
+
const {Transform} = require ('stream')
|
|
22
|
+
|
|
23
|
+
class DbCall extends EventEmitter {
|
|
24
|
+
|
|
25
|
+
constructor (db, sql, params = [], options = {}) {
|
|
26
|
+
|
|
27
|
+
if (!db) throw Error ('DbCall: db must be defined')
|
|
28
|
+
|
|
29
|
+
super ()
|
|
30
|
+
|
|
31
|
+
this.ord = ++ db.count
|
|
32
|
+
|
|
33
|
+
if (typeof sql !== 'string') throw Error ('DbCall: sql must be a string, found: ' + sql)
|
|
34
|
+
|
|
35
|
+
if (!('maxRows' in options)) options.maxRows = 0
|
|
36
|
+
|
|
37
|
+
if (typeof options.maxRows !== 'number') throw Error ('DbCall: maxRows must be a number, found: ' + options.maxRows)
|
|
38
|
+
|
|
39
|
+
if (options.maxRows < 0) throw Error ('DbCall: maxRows cannot be negative, found: ' + options.maxRows)
|
|
40
|
+
|
|
41
|
+
if (options.maxRows > 0) {
|
|
42
|
+
|
|
43
|
+
if (!('rowMode' in options)) options.rowMode = MD_OBJECT
|
|
44
|
+
|
|
45
|
+
switch (options.rowMode) {
|
|
46
|
+
|
|
47
|
+
case MD_ARRAY:
|
|
48
|
+
case MD_OBJECT:
|
|
49
|
+
case MD_SCALAR:
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
default:
|
|
53
|
+
throw Error ('DbCall: rowMode must be one of (scalar|array|object) found' + options.rowMode)
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!('checkOverflow' in options)) options.checkOverflow = false
|
|
58
|
+
|
|
59
|
+
if (typeof options.checkOverflow !== 'boolean') throw Error ('DbCall: checkOverflow must be a boolean, found: ' + options.maxRows)
|
|
60
|
+
|
|
61
|
+
if (options.maxRows === Infinity) {
|
|
62
|
+
|
|
63
|
+
if ('minRows' in options) throw Error ('DbCall: minRows requires a finite maxRows')
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
|
|
68
|
+
if (!('minRows' in options)) options.minRows = 0
|
|
69
|
+
|
|
70
|
+
if (typeof options.minRows !== 'number') throw Error ('DbCall: minRows must be a number, found: ' + options.minRows)
|
|
71
|
+
|
|
72
|
+
if (options.minRows < 0) throw Error ('DbCall: minRows cannot be negative, found: ' + options.minRows)
|
|
73
|
+
|
|
74
|
+
if (options.minRows > options.maxRows) throw Error ('DbCall: minRows cannot exceed maxRows=' + options.maxRows)
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
|
|
81
|
+
for (const k of OPT_REQUIRING_MAX_ROW) if (k in options) throw Error (`DbCall: ${k} cannot be set with maxRows=${options.maxRows}`)
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if ('notFound' in options && !(options.minRows > 0)) throw Error ('DbCall: notFound requires a positive minRows')
|
|
86
|
+
|
|
87
|
+
this.db = db
|
|
88
|
+
this.sql = sql
|
|
89
|
+
this.params = params
|
|
90
|
+
this.options = options
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get objectMode () {
|
|
95
|
+
|
|
96
|
+
return this.options.rowMode === MD_OBJECT
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get result () {
|
|
101
|
+
|
|
102
|
+
const {rows, options} = this, {minRows, maxRows} = options
|
|
103
|
+
|
|
104
|
+
if (maxRows === 0) return
|
|
105
|
+
|
|
106
|
+
if (minRows > rows.length) {
|
|
107
|
+
|
|
108
|
+
if ('notFound' in options) return options.notFound
|
|
109
|
+
|
|
110
|
+
throw Error (`Missing data: ${rows.length} found, ${minRows} required`)
|
|
111
|
+
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return maxRows === 1 ? rows [0] : rows
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
finish () {
|
|
119
|
+
|
|
120
|
+
this.emit (EV_FINISH)
|
|
121
|
+
|
|
122
|
+
this.removeAllListeners (EV_FINISH)
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
flattenArray () {
|
|
127
|
+
|
|
128
|
+
const {rows} = this
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < rows.length; i ++) rows [i] = rows [i] [0]
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
flattenStream () {
|
|
135
|
+
|
|
136
|
+
const {rows} = this
|
|
137
|
+
|
|
138
|
+
const xform = new Transform ({objectMode: true,
|
|
139
|
+
transform (r, __, cb) {
|
|
140
|
+
const v = r [0]
|
|
141
|
+
cb (null, v == null ? NULL : v)
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
rows.once ('error', e => xform.destroy (e))
|
|
146
|
+
|
|
147
|
+
this.rows = this.rows.pipe (xform)
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
observeStream () {
|
|
152
|
+
|
|
153
|
+
const {rows} = this
|
|
154
|
+
|
|
155
|
+
rows.on (EV_ERROR, e => this.emit (EV_ERROR, e))
|
|
156
|
+
|
|
157
|
+
const finish = () => this.finish ()
|
|
158
|
+
|
|
159
|
+
for (const event of EV_END_STREAM) rows.once (event, finish)
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
processArray () {
|
|
164
|
+
|
|
165
|
+
if (this.options.rowMode === MD_SCALAR) this.flattenArray ()
|
|
166
|
+
|
|
167
|
+
this.finish ()
|
|
168
|
+
|
|
169
|
+
return this.result
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
processStream () {
|
|
174
|
+
|
|
175
|
+
const {rowMode} = this.options
|
|
176
|
+
|
|
177
|
+
if (rowMode === MD_SCALAR) this.flattenStream ()
|
|
178
|
+
|
|
179
|
+
this.observeStream ()
|
|
180
|
+
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async fetchStream () {
|
|
184
|
+
|
|
185
|
+
const a = [], {rows, options: {maxRows, checkOverflow}} = this; await new Promise ((ok, fail) => {
|
|
186
|
+
|
|
187
|
+
rows.once ('error', fail)
|
|
188
|
+
|
|
189
|
+
let stop = false; rows.on ('end', () => ok (stop = true))
|
|
190
|
+
|
|
191
|
+
rows.on ('data',
|
|
192
|
+
|
|
193
|
+
checkOverflow ? r => {
|
|
194
|
+
if (a.length === maxRows) return rows.destroy (Error ('Result set overflow, maxRows = ' + maxRows))
|
|
195
|
+
a.push (mayBeNull (r))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
: r => {
|
|
199
|
+
if (stop) return
|
|
200
|
+
a.push (mayBeNull (r))
|
|
201
|
+
if (a.length === maxRows) rows.emit ('end')
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
}).finally (() => rows.destroy ())
|
|
207
|
+
|
|
208
|
+
this.rows = a
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async exec () {
|
|
213
|
+
|
|
214
|
+
const {db} = this
|
|
215
|
+
|
|
216
|
+
db.lang.normalizeSQL (this)
|
|
217
|
+
|
|
218
|
+
this.once ('error', () => this.finish ())
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
|
|
222
|
+
const {maxRows} = this.options
|
|
223
|
+
|
|
224
|
+
this.emit ('start')
|
|
225
|
+
|
|
226
|
+
await db.exec (this)
|
|
227
|
+
|
|
228
|
+
this.emit ('result')
|
|
229
|
+
|
|
230
|
+
if (maxRows === 0) return
|
|
231
|
+
|
|
232
|
+
if (Array.isArray (this.rows)) return this.processArray ()
|
|
233
|
+
|
|
234
|
+
this.processStream ()
|
|
235
|
+
|
|
236
|
+
if (maxRows === Infinity) return this.result
|
|
237
|
+
|
|
238
|
+
await this.fetchStream ()
|
|
239
|
+
|
|
240
|
+
return this.result
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
|
|
245
|
+
this.emit ('error', this.error = error)
|
|
246
|
+
|
|
247
|
+
throw error
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = DbCall
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const {LifeCycleTracker} = require ('doix')
|
|
2
|
+
const stringEscape = require ('string-escape-map')
|
|
3
|
+
|
|
4
|
+
const ESC_PARAMS = new stringEscape ([
|
|
5
|
+
['\t', '\\t'],
|
|
6
|
+
['\n', '\\n'],
|
|
7
|
+
['\r', '\\r'],
|
|
8
|
+
[ "'", "''"],
|
|
9
|
+
])
|
|
10
|
+
|
|
11
|
+
const stringifyParams = params => {
|
|
12
|
+
|
|
13
|
+
if (!Array.isArray (params)) return JSON.stringify (params)
|
|
14
|
+
|
|
15
|
+
if (params.length === 0) return ''
|
|
16
|
+
|
|
17
|
+
let s = '['; for (const p of params) {
|
|
18
|
+
|
|
19
|
+
if (s.length !== 1) s += ', '
|
|
20
|
+
|
|
21
|
+
if (typeof p === 'string') {
|
|
22
|
+
|
|
23
|
+
s += "'"
|
|
24
|
+
s += ESC_PARAMS.escape (p)
|
|
25
|
+
s += "'"
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
|
|
30
|
+
s += p
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return s + ']'
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class DbCallTracker extends LifeCycleTracker {
|
|
41
|
+
|
|
42
|
+
constructor (call) {
|
|
43
|
+
|
|
44
|
+
const {db} = call
|
|
45
|
+
|
|
46
|
+
super (call, db.pool.logger)
|
|
47
|
+
|
|
48
|
+
this.call = call
|
|
49
|
+
|
|
50
|
+
this.prefix = db.job.tracker.prefix + '/' + db.uuid + '/' + call.ord
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
startMessage () {
|
|
55
|
+
|
|
56
|
+
const {sql, params} = this.call
|
|
57
|
+
|
|
58
|
+
const s = super.startMessage () + ' ' + sql
|
|
59
|
+
|
|
60
|
+
if (params.length === 0) return s
|
|
61
|
+
|
|
62
|
+
return s + ' ' + stringifyParams (params)
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
DbCallTracker.stringifyParams = stringifyParams
|
|
69
|
+
|
|
70
|
+
module.exports = DbCallTracker
|
package/lib/DbClient.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
const EventEmitter = require ('events')
|
|
2
2
|
const {randomUUID} = require ('crypto')
|
|
3
3
|
|
|
4
|
+
const DbCall = require ('./DbCall.js')
|
|
4
5
|
const DbQuery = require ('./query/DbQuery.js')
|
|
5
6
|
const DbMigrationPlan = require ('./migration/DbMigrationPlan.js')
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const PR_QUERY = Symbol.for ('query')
|
|
9
|
+
const PR_CALL = Symbol.for ('call')
|
|
10
|
+
const PR_COLUMNS = Symbol.for ('columns')
|
|
11
|
+
const PR_COUNT = Symbol.for ('count')
|
|
12
|
+
|
|
13
|
+
const set = (o, k, v) => Object.defineProperty (o, k, {
|
|
14
|
+
configurable: false,
|
|
15
|
+
enumerable: false,
|
|
16
|
+
get: () => v
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const setAll = (rows, call, q) => {
|
|
20
|
+
set (rows, PR_CALL, call)
|
|
21
|
+
set (rows, PR_COLUMNS, call.columns)
|
|
22
|
+
if (q instanceof DbQuery) set (rows, PR_QUERY, q)
|
|
23
|
+
}
|
|
8
24
|
|
|
9
25
|
class DbClient extends EventEmitter {
|
|
10
26
|
|
|
11
27
|
constructor () {
|
|
12
28
|
|
|
13
29
|
super ()
|
|
30
|
+
|
|
31
|
+
this.count = 0
|
|
14
32
|
|
|
15
33
|
this.uuid = randomUUID ()
|
|
16
34
|
|
|
@@ -22,106 +40,122 @@ class DbClient extends EventEmitter {
|
|
|
22
40
|
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
async
|
|
43
|
+
async getArrayOnly (q, params = [], o = {}) {
|
|
26
44
|
|
|
27
|
-
const
|
|
28
|
-
const isPartial = options.isPartial === true
|
|
29
|
-
|
|
30
|
-
const s = await this.getStream (sql, params, options)
|
|
31
|
-
const rows = []
|
|
45
|
+
const options = {...o}
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
for await (const r of s) {
|
|
47
|
+
if (!('maxRows' in options)) {
|
|
48
|
+
options.maxRows = 1000
|
|
49
|
+
options.checkOverflow = true
|
|
50
|
+
}
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
const call = this.call (q, params, options)
|
|
42
53
|
|
|
43
|
-
|
|
54
|
+
await call.exec ()
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
const {rows} = call
|
|
46
57
|
|
|
47
|
-
|
|
58
|
+
setAll (rows, call, q)
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
return rows
|
|
50
61
|
|
|
51
|
-
|
|
62
|
+
}
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
s.destroy ()
|
|
64
|
+
async getArray (q, p, o) {
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
66
|
+
const arrayOnly = this.getArrayOnly (q, p, o)
|
|
60
67
|
|
|
61
|
-
|
|
68
|
+
if (!(q instanceof DbQuery) || !('offset' in q.options)) return arrayOnly
|
|
62
69
|
|
|
70
|
+
const [rows, cnt] = await Promise.all ([
|
|
71
|
+
arrayOnly,
|
|
72
|
+
this.getScalar (q.toQueryCount ())
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
set (rows, PR_COUNT, parseInt (cnt))
|
|
76
|
+
|
|
63
77
|
return rows
|
|
64
78
|
|
|
65
79
|
}
|
|
80
|
+
|
|
81
|
+
async getObject (sql, params = [], options = {}) {
|
|
66
82
|
|
|
67
|
-
|
|
83
|
+
const {model} = this; if (model) {
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const addCount = 'offset' in q.options, todo = addCount ? [null, this.getScalar (q.toQueryCount ())] : []
|
|
72
|
-
|
|
73
|
-
const params = this.lang.toParamsSql (q), sql = params.pop (); todo [0] = this.getArrayBySql (sql, params, o)
|
|
85
|
+
const relation = model.find (sql); if (relation) {
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
const {name, pk} = relation, filters = []; for (let i = 0; i < pk.length; i ++) filters.push ([pk [i], '=', params [i]])
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
configurable: false,
|
|
79
|
-
enumerable: false,
|
|
80
|
-
get: () => q
|
|
81
|
-
})
|
|
89
|
+
sql = model.createQuery ([[name, {filters}]])
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
}
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
}
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
return this.call (sql, params, {
|
|
96
|
+
...options,
|
|
97
|
+
minRows: 1,
|
|
98
|
+
maxRows: 1,
|
|
99
|
+
}).exec ()
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getScalar (sql, params = [], options = {}) {
|
|
104
|
+
|
|
105
|
+
return this.call (sql, params, {
|
|
106
|
+
notFound: undefined,
|
|
107
|
+
...options,
|
|
108
|
+
rowMode: 'scalar',
|
|
109
|
+
minRows: 1,
|
|
110
|
+
maxRows: 1,
|
|
111
|
+
}).exec ()
|
|
94
112
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
113
|
+
}
|
|
98
114
|
|
|
99
|
-
async
|
|
100
|
-
|
|
101
|
-
const params = this.lang.genSelectObjectParamsSql (sqlOrName, p), sql = params.pop ()
|
|
115
|
+
async do (sql, params = [], options = {}) {
|
|
102
116
|
|
|
103
|
-
const
|
|
117
|
+
const call = this.call (sql, params, {
|
|
104
118
|
...options,
|
|
105
|
-
maxRows:
|
|
106
|
-
isPartial: true,
|
|
119
|
+
maxRows: 0,
|
|
107
120
|
})
|
|
108
121
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return 'notFound' in options ? notFound : {}
|
|
122
|
+
await call.exec ()
|
|
123
|
+
|
|
124
|
+
return call
|
|
114
125
|
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
async
|
|
128
|
+
async getStream (q, p = [], options = {}) {
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
notFound: undefined,
|
|
130
|
+
const call = this.call (q, p, {
|
|
121
131
|
...options,
|
|
122
|
-
|
|
132
|
+
maxRows: Infinity,
|
|
123
133
|
})
|
|
124
134
|
|
|
135
|
+
const rows = await call.exec ()
|
|
136
|
+
|
|
137
|
+
setAll (rows, call, q)
|
|
138
|
+
|
|
139
|
+
return rows
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
call (q, params, options) {
|
|
144
|
+
|
|
145
|
+
if (q instanceof DbQuery) {
|
|
146
|
+
|
|
147
|
+
params = this.lang.toParamsSql (q)
|
|
148
|
+
|
|
149
|
+
q = params.pop ()
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const call = new DbCall (this, q, params, options)
|
|
154
|
+
|
|
155
|
+
call.tracker = new this.pool.trackerClass (call)
|
|
156
|
+
|
|
157
|
+
return call
|
|
158
|
+
|
|
125
159
|
}
|
|
126
160
|
|
|
127
161
|
}
|
package/lib/DbLang.js
CHANGED
|
@@ -263,37 +263,6 @@ class DbLang {
|
|
|
263
263
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
genSelectObjectParamsSql (sqlOrName, params) {
|
|
267
|
-
|
|
268
|
-
if (!Array.isArray (params)) params = [params]
|
|
269
|
-
|
|
270
|
-
const {model} = this
|
|
271
|
-
|
|
272
|
-
const relation = model ? model.find (sqlOrName) : undefined; if (relation) {
|
|
273
|
-
|
|
274
|
-
const {qName, pk, columns} = relation
|
|
275
|
-
|
|
276
|
-
let filter = ''; for (const name of pk) {
|
|
277
|
-
|
|
278
|
-
if (filter.length !== 0) filter += ' AND '
|
|
279
|
-
|
|
280
|
-
filter += columns [name].qName + '=?'
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
params.push (`SELECT * FROM ${qName} WHERE ${filter}`)
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
|
|
289
|
-
params.push (sqlOrName)
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return params
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
266
|
genInsertParamsSql (name, data) {
|
|
298
267
|
|
|
299
268
|
const {model} = this; if (!model) throw Error ('Model not set')
|
|
@@ -510,6 +479,30 @@ class DbLang {
|
|
|
510
479
|
|
|
511
480
|
}
|
|
512
481
|
|
|
482
|
+
normalizeSQL (call) {
|
|
483
|
+
|
|
484
|
+
call.sql = call.sql.trim ()
|
|
485
|
+
|
|
486
|
+
const {sql} = call, {length} = sql; if (length === 0) return
|
|
487
|
+
|
|
488
|
+
let r = '', from = 0
|
|
489
|
+
|
|
490
|
+
while (from < length) {
|
|
491
|
+
|
|
492
|
+
let to = from + 1; while (to < length && sql.charCodeAt (to) > 32) to ++
|
|
493
|
+
|
|
494
|
+
if (r.length !== 0) r += ' '
|
|
495
|
+
|
|
496
|
+
r += sql.slice (from, to)
|
|
497
|
+
|
|
498
|
+
from = to; while (from < length && sql.charCodeAt (from) <= 32) from ++
|
|
499
|
+
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
call.sql = r
|
|
503
|
+
|
|
504
|
+
}
|
|
505
|
+
|
|
513
506
|
}
|
|
514
507
|
|
|
515
508
|
module.exports = DbLang
|
package/lib/DbPool.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const {ResourcePool} = require ('doix')
|
|
2
|
-
const
|
|
2
|
+
const DbCallTracker = require ('./DbCallTracker.js')
|
|
3
3
|
|
|
4
4
|
class DbPool extends ResourcePool {
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ class DbPool extends ResourcePool {
|
|
|
12
12
|
|
|
13
13
|
this.logger = o.logger
|
|
14
14
|
|
|
15
|
-
this.
|
|
15
|
+
this.trackerClass = o.trackerClass || DbCallTracker
|
|
16
16
|
|
|
17
17
|
}
|
|
18
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doix-db",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Shared database related code for doix",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"homepage": "https://github.com/do-/node-doix-db#readme",
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"doix": "^1.0.
|
|
43
|
+
"doix": "^1.0.5"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"jest": "^29.6.1"
|
package/lib/DbEventLogger.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const {EventLogger} = require ('doix')
|
|
2
|
-
const stringEscape = require ('string-escape-map')
|
|
3
|
-
|
|
4
|
-
const ESC_PARAMS = new stringEscape ([
|
|
5
|
-
['\t', '\\t'],
|
|
6
|
-
['\n', '\\n'],
|
|
7
|
-
['\r', '\\r'],
|
|
8
|
-
[ "'", "''"],
|
|
9
|
-
])
|
|
10
|
-
|
|
11
|
-
const normalizeSpace = s => {
|
|
12
|
-
|
|
13
|
-
s = s.trim ();
|
|
14
|
-
|
|
15
|
-
const {length} = s; if (length === 0) return s
|
|
16
|
-
|
|
17
|
-
let r = '', from = 0
|
|
18
|
-
|
|
19
|
-
while (from < length) {
|
|
20
|
-
|
|
21
|
-
let to = from + 1; while (to < length && s.charCodeAt (to) > 32) to ++
|
|
22
|
-
|
|
23
|
-
if (r.length !== 0) r += ' '
|
|
24
|
-
|
|
25
|
-
r += s.slice (from, to)
|
|
26
|
-
|
|
27
|
-
from = to; while (from < length && s.charCodeAt (from) <= 32) from ++
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return r
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const stringifyParams = params => {
|
|
36
|
-
|
|
37
|
-
if (!Array.isArray (params)) return JSON.stringify (params)
|
|
38
|
-
|
|
39
|
-
if (params.length === 0) return ''
|
|
40
|
-
|
|
41
|
-
let s = '['; for (const p of params) {
|
|
42
|
-
|
|
43
|
-
if (s.length !== 1) s += ', '
|
|
44
|
-
|
|
45
|
-
if (typeof p === 'string') {
|
|
46
|
-
|
|
47
|
-
s += "'"
|
|
48
|
-
s += ESC_PARAMS.escape (p)
|
|
49
|
-
s += "'"
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
|
|
54
|
-
s += p
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return s + ']'
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
class DbEventLogger extends EventLogger {
|
|
65
|
-
|
|
66
|
-
constructor (client) {
|
|
67
|
-
|
|
68
|
-
super (client)
|
|
69
|
-
|
|
70
|
-
this.client = client
|
|
71
|
-
|
|
72
|
-
this.logger = client.logger
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get prefix () {
|
|
77
|
-
|
|
78
|
-
let {client} = this, {job} = client
|
|
79
|
-
|
|
80
|
-
let p = job.uuid + '/' + client.uuid
|
|
81
|
-
|
|
82
|
-
while (job = job.parent) p = job.uuid + '/' + p
|
|
83
|
-
|
|
84
|
-
return p
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
startMessage ({sql, params}) {
|
|
89
|
-
|
|
90
|
-
const s = '> ' + normalizeSpace (sql), p = stringifyParams (params)
|
|
91
|
-
|
|
92
|
-
return this.message (p.length === 0 ? s : s + ' ' + p)
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
DbEventLogger.normalizeSpace = normalizeSpace
|
|
99
|
-
DbEventLogger.stringifyParams = stringifyParams
|
|
100
|
-
|
|
101
|
-
module.exports = DbEventLogger
|