doix-db 1.0.23 → 1.0.26

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/index.js CHANGED
@@ -4,6 +4,7 @@ module.exports = {
4
4
  DbClient: require ('./lib/DbClient.js'),
5
5
  DbPool: require ('./lib/DbPool.js'),
6
6
  DbLang: require ('./lib/DbLang.js'),
7
+ DbCsvPrinter: require ('./lib/DbCsvPrinter.js'),
7
8
  DbColumn: require ('./lib/model/DbColumn.js'),
8
9
  DbReference: require ('./lib/model/DbReference.js'),
9
10
  DbObjectMerger: require ('./lib/model/DbObjectMerger.js'),
@@ -23,6 +24,8 @@ module.exports = {
23
24
  DbTypeArithmeticFloat: require ('./lib/model/types/DbTypeArithmeticFloat.js'),
24
25
  DbTypeArithmeticInt: require ('./lib/model/types/DbTypeArithmeticInt.js'),
25
26
  DbTypeCharacter: require ('./lib/model/types/DbTypeCharacter.js'),
27
+ DbTypeDate: require ('./lib/model/types/DbTypeDate.js'),
28
+ DbTypeTimestamp: require ('./lib/model/types/DbTypeTimestamp.js'),
26
29
 
27
30
  DbMigrationPlan:require ('./lib/migration/DbMigrationPlan.js'),
28
31
  DbQuery: require ('./lib/query/DbQuery.js'),
package/lib/DbClient.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const EventEmitter = require ('events')
2
2
  const {randomUUID} = require ('crypto')
3
3
 
4
+ const DbCsvPrinter = require ('./DbCsvPrinter.js')
4
5
  const DbCall = require ('./DbCall.js')
5
6
  const DbQuery = require ('./query/DbQuery.js')
6
7
  const DbMigrationPlan = require ('./migration/DbMigrationPlan.js')
@@ -33,6 +34,22 @@ class DbClient extends EventEmitter {
33
34
  this.uuid = randomUUID ()
34
35
 
35
36
  }
37
+
38
+ async createTempTable (table, options) {
39
+
40
+ if (typeof table === 'string') table = this.model.find (table)
41
+
42
+ return this.do (this.lang.genCreateTempTable (table, options))
43
+
44
+ }
45
+
46
+ toCsv (options) {
47
+
48
+ const {lang} = this
49
+
50
+ return new DbCsvPrinter ({...options, lang})
51
+
52
+ }
36
53
 
37
54
  createMigrationPlan () {
38
55
 
@@ -0,0 +1,239 @@
1
+ const DbColumn = require ('./model/DbColumn.js')
2
+ const DbTypeArithmetic = require ('./model/types/DbTypeArithmetic.js')
3
+ const DbTypeArithmeticInt = require ('./model/types/DbTypeArithmeticInt.js')
4
+ const DbTypeArithmeticFixed = require ('./model/types/DbTypeArithmeticFixed.js')
5
+ const DbTypeCharacter = require ('./model/types/DbTypeCharacter.js')
6
+ const DbTypeTemporal = require ('./model/types/DbTypeTemporal.js')
7
+ const DbTypeDate = require ('./model/types/DbTypeDate.js')
8
+ const DbTypeTimestamp = require ('./model/types/DbTypeTimestamp.js')
9
+ const {Transform} = require ('stream')
10
+ const stringEscape = require ('string-escape-map')
11
+ const CH_QQ = '"', QQ_ESC = new stringEscape ([[CH_QQ, CH_QQ + CH_QQ]])
12
+
13
+ const FMT_ISO_DATE = new Intl.DateTimeFormat ('sv', {
14
+ year: 'numeric',
15
+ month: '2-digit',
16
+ day: '2-digit',
17
+ })
18
+
19
+ const FMT_ISO_TIMESTAMP = new Intl.DateTimeFormat ('sv', {
20
+ year: 'numeric',
21
+ month: '2-digit',
22
+ day: '2-digit',
23
+ hour: '2-digit',
24
+ minute: '2-digit',
25
+ second: '2-digit',
26
+ fractionalSecondDigits: 3
27
+ })
28
+
29
+ class DbCsvPrinter extends Transform {
30
+
31
+ constructor (options = {}) {
32
+
33
+ let {table, columns, lang, NULL} = options
34
+
35
+ super ({writableObjectMode: true})
36
+
37
+ this.NULL = typeof NULL === 'string' ? NULL : ''
38
+
39
+ if (!columns) {
40
+ if (!table) throw Error (`Either table or columns must be defined`)
41
+ columns = Object.values (table.columns)
42
+ }
43
+
44
+ if (lang) {
45
+
46
+ this.lang = lang
47
+
48
+ }
49
+ else {
50
+
51
+ if (table) {
52
+ this.lang = table.model.lang
53
+ }
54
+ else {
55
+ throw Error (`Lang not defined`)
56
+ }
57
+
58
+ }
59
+
60
+ if (!Array.isArray (columns)) columns = Object.entries (columns).map (([name, def]) => {
61
+
62
+ const col = new DbColumn (def)
63
+
64
+ col.name = name
65
+
66
+ return col
67
+
68
+ })
69
+
70
+ const {length} = columns; for (let i = 0; i < length; i ++) {
71
+
72
+ const col = columns [i];
73
+
74
+ if (typeof col === 'string') {
75
+
76
+ if (!table) throw Error ('To pass columns by name only, the table must be provided')
77
+
78
+ if (!(col in table.columns)) throw Error (`${table.name}.${col} not found`)
79
+
80
+ columns [i] = table.columns [col]
81
+
82
+ }
83
+
84
+ if (!('typeDef' in columns [i])) columns [i].setLang (this.lang)
85
+
86
+ }
87
+
88
+ this.columns = columns
89
+
90
+ }
91
+
92
+ _transform (r, _, callback) {
93
+
94
+ const {columns} = this, {length} = columns
95
+
96
+ try {
97
+
98
+ let s = ''; for (let i = 0; i < length; i ++) {
99
+
100
+ if (i !== 0) s += ','
101
+
102
+ const column = columns [i], {name, typeDef} = column
103
+
104
+ const value = r [name], v = value == null ? column.default : value
105
+
106
+ switch (v) {
107
+ case Number.POSITIVE_INFINITY:
108
+ case Number.NEGATIVE_INFINITY:
109
+ throw Error (`${column.name}: infinite values not supported`)
110
+ }
111
+
112
+ if (v == null) {
113
+
114
+ if (!column.nullable) throw Error (`${name} doesn't allow null values`)
115
+
116
+ s += this.NULL
117
+
118
+ }
119
+ else if (typeof v === 'boolean') {
120
+
121
+ s += v ? '1' : '0'
122
+
123
+ }
124
+ else if (typeDef instanceof DbTypeCharacter) {
125
+
126
+ s += CH_QQ
127
+ s += QQ_ESC.escape (String (v))
128
+ s += CH_QQ
129
+
130
+ }
131
+ else if (typeDef instanceof DbTypeArithmetic) {
132
+
133
+ const n = Number (v)
134
+
135
+ if (typeDef instanceof DbTypeArithmeticInt) {
136
+
137
+ if (!Number.isInteger (Number (v))) throw Error (`${column.name} must be integer, found ${value}`)
138
+
139
+ s += v
140
+
141
+ }
142
+ else if (typeDef instanceof DbTypeArithmeticFixed) {
143
+
144
+ if (!Number.isFinite (n)) throw Error (`${column.name} must be a finite number, found ${value}`)
145
+
146
+ s += n.toFixed (column.scale)
147
+
148
+ }
149
+ else {
150
+
151
+ if (Number.isNaN (n)) throw Error (`${column.name} must be a number, found ${value}`)
152
+
153
+ s += v
154
+
155
+ }
156
+
157
+ }
158
+ else if (typeDef instanceof DbTypeTemporal) {
159
+
160
+ const isDate = typeDef instanceof DbTypeDate; if (!isDate && !(typeDef instanceof DbTypeTimestamp)) throw Error (`Don't know how to format ${column.name}`)
161
+
162
+ let d; if (typeof v === 'string') {
163
+
164
+ d = v
165
+
166
+ }
167
+ else if (v instanceof Date) {
168
+
169
+ d = (isDate ? FMT_ISO_DATE : FMT_ISO_TIMESTAMP).format (v)
170
+
171
+ }
172
+ else {
173
+
174
+ throw Error (`${column.name} must be a date/time, found ${value}`)
175
+
176
+ }
177
+
178
+ if (isDate) {
179
+
180
+ const {length} = d
181
+
182
+ if (length < 10) {
183
+ throw Error (`${column.name} must be a date, found ${value}`)
184
+ }
185
+ else if (length === 10) {
186
+ s += d
187
+ }
188
+ else {
189
+ s += d.slice (0, 10)
190
+ }
191
+
192
+ }
193
+ else {
194
+
195
+ const {length} = d, {scale} = column
196
+
197
+ if (length < 19) {
198
+
199
+ throw Error (`${column.name} must be a timestamp, found ${value}`)
200
+
201
+ }
202
+ else {
203
+
204
+ s += d.slice (0, 19)
205
+
206
+ if (scale > 0 && length > 20) {
207
+
208
+ s += '.'
209
+ s += d.slice (20, 20 + scale)
210
+
211
+ }
212
+
213
+ }
214
+
215
+ }
216
+
217
+ }
218
+ else {
219
+
220
+ throw Error (`Don't know how to format ${column.name}`)
221
+
222
+ }
223
+
224
+ }
225
+
226
+ s += '\n'; callback (null, s)
227
+
228
+ }
229
+ catch (err) {
230
+
231
+ callback (err)
232
+
233
+ }
234
+
235
+ }
236
+
237
+ }
238
+
239
+ module.exports = DbCsvPrinter
package/lib/DbLang.js CHANGED
@@ -7,11 +7,12 @@ const DbProcedure = require ('./model/DbProcedure.js')
7
7
  const DbFunction = require ('./model/DbFunction.js')
8
8
 
9
9
  const DbType = require ('./model/types/DbType.js')
10
- const DbTypeArithmetic = require ('./model/types/DbTypeArithmetic.js')
11
10
  const DbTypeArithmeticFixed = require ('./model/types/DbTypeArithmeticFixed.js')
12
11
  const DbTypeArithmeticFloat = require ('./model/types/DbTypeArithmeticFloat.js')
13
12
  const DbTypeArithmeticInt = require ('./model/types/DbTypeArithmeticInt.js')
14
13
  const DbTypeCharacter = require ('./model/types/DbTypeCharacter.js')
14
+ const DbTypeDate = require ('./model/types/DbTypeDate.js')
15
+ const DbTypeTimestamp = require ('./model/types/DbTypeTimestamp.js')
15
16
 
16
17
  const CH_Q = "'", CH_QQ = '"'
17
18
 
@@ -27,13 +28,15 @@ const DELIM_UPDATE = [',', ' AND ']
27
28
 
28
29
  class DbLang {
29
30
 
30
- static TP_SMALLINT = new DbTypeArithmeticInt ({name: 'SMALLINT', bytes: 2})
31
- static TP_INT = new DbTypeArithmeticInt ({name: 'INT', bytes: 4})
32
- static TP_BIGINT = new DbTypeArithmeticInt ({name: 'BIGINT', bytes: 8})
33
- static TP_REAL = new DbTypeArithmeticFloat ({name: 'REAL'})
34
- static TP_NUMERIC = new DbTypeArithmeticFixed ({name: 'NUMERIC'})
35
- static TP_CHAR = new DbTypeCharacter ({name: 'CHAR'})
36
- static TP_VARCHAR = new DbTypeCharacter ({name: 'VARCHAR'})
31
+ static TP_SMALLINT = new DbTypeArithmeticInt ({name: 'SMALLINT', bytes: 2})
32
+ static TP_INT = new DbTypeArithmeticInt ({name: 'INT', bytes: 4})
33
+ static TP_BIGINT = new DbTypeArithmeticInt ({name: 'BIGINT', bytes: 8})
34
+ static TP_REAL = new DbTypeArithmeticFloat ({name: 'REAL'})
35
+ static TP_NUMERIC = new DbTypeArithmeticFixed ({name: 'NUMERIC'})
36
+ static TP_CHAR = new DbTypeCharacter ({name: 'CHAR'})
37
+ static TP_VARCHAR = new DbTypeCharacter ({name: 'VARCHAR'})
38
+ static TP_DATE = new DbTypeDate ({name: 'DATE'})
39
+ static TP_TIMESTAMP = new DbTypeTimestamp ({name: 'TIMESTAMP'})
37
40
 
38
41
  getTypeDefinition (name) {
39
42
 
@@ -64,6 +67,12 @@ class DbLang {
64
67
  case 'REAL':
65
68
  return DbLang.TP_REAL
66
69
 
70
+ case 'DATE':
71
+ return DbLang.TP_DATE
72
+
73
+ case 'TIMESTAMP':
74
+ return DbLang.TP_TIMESTAMP
75
+
67
76
  }
68
77
 
69
78
  return new DbType ({name})
@@ -467,6 +476,16 @@ class DbLang {
467
476
 
468
477
  }
469
478
 
479
+ genCreateTempTable ({qName, columns}, options = {}) {
480
+
481
+ let sql = 'CREATE TEMPORARY TABLE '
482
+
483
+ if (options.onlyIfMissing) sql += 'IF NOT EXISTS '
484
+
485
+ return sql + qName + ' (' + Object.values (columns).map (c => this.genColumnDefinition (c)) + ')'
486
+
487
+ }
488
+
470
489
  getTriggerName (table, i) {
471
490
 
472
491
  return table.name + '__trg_' + i.toString ().padStart ((table.triggers.length - 1).toString ().length, '0')
@@ -0,0 +1,13 @@
1
+ const DbTypeTemporal = require ('./DbTypeTemporal.js')
2
+
3
+ class DbTypeDate extends DbTypeTemporal {
4
+
5
+ constructor (o) {
6
+
7
+ super (o)
8
+
9
+ }
10
+
11
+ }
12
+
13
+ module.exports = DbTypeDate
@@ -0,0 +1,13 @@
1
+ const DbType = require ('./DbType.js')
2
+
3
+ class DbTypeTemporal extends DbType {
4
+
5
+ constructor (o) {
6
+
7
+ super (o)
8
+
9
+ }
10
+
11
+ }
12
+
13
+ module.exports = DbTypeTemporal
@@ -0,0 +1,13 @@
1
+ const DbTypeTemporal = require ('./DbTypeTemporal.js')
2
+
3
+ class DbTypeTimestamp extends DbTypeTemporal {
4
+
5
+ constructor (o) {
6
+
7
+ super (o)
8
+
9
+ }
10
+
11
+ }
12
+
13
+ module.exports = DbTypeTimestamp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doix-db",
3
- "version": "1.0.23",
3
+ "version": "1.0.26",
4
4
  "description": "Shared database related code for doix",
5
5
  "main": "index.js",
6
6
  "files": [