pwi-plata-type 0.4.166 → 0.4.167

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.
Files changed (101) hide show
  1. package/README.md +2 -2
  2. package/__BUILD__/libs/sql.js +4 -1
  3. package/__BUILD__/libs/sql.js.map +1 -1
  4. package/package.json +53 -53
  5. package/src/@types/build.d.ts +26 -26
  6. package/src/@types/cluster.actions.d.ts +27 -27
  7. package/src/@types/cluster.d.ts +55 -55
  8. package/src/@types/create-cli.d.ts +8 -8
  9. package/src/@types/exported.d.ts +31 -31
  10. package/src/@types/fs.d.ts +6 -6
  11. package/src/@types/global.d.ts +113 -113
  12. package/src/@types/knex.d.ts +13 -13
  13. package/src/@types/model.d.ts +143 -143
  14. package/src/@types/router.d.ts +107 -107
  15. package/src/@types/sql.d.ts +75 -75
  16. package/src/@types/swagger.d.ts +22 -22
  17. package/src/bin/extras/create-api-cli.ts +218 -218
  18. package/src/bin/extras/create-cluster-cli.ts +160 -160
  19. package/src/bin/extras/create-lib-cli.ts +84 -84
  20. package/src/bin/extras/plata-create-tools.ts +97 -97
  21. package/src/bin/plata-api.ts +189 -189
  22. package/src/bin/plata-build.ts +17 -17
  23. package/src/bin/plata-cluster.ts +42 -42
  24. package/src/bin/plata-cmd.ts +18 -18
  25. package/src/bin/plata-create.ts +49 -49
  26. package/src/bin/plata-runtime.ts +131 -131
  27. package/src/bin/plata-swagger.ts +99 -99
  28. package/src/bin/runtime/_setupRuntime.ts +48 -48
  29. package/src/bin/runtime/_types.d.ts +7 -7
  30. package/src/bin/runtime/class.ts +20 -20
  31. package/src/bin/runtime/dirs.ts +25 -25
  32. package/src/bin/runtime/effect.ts +60 -60
  33. package/src/bin/runtime/fs.ts +10 -10
  34. package/src/bin/runtime/promises.ts +18 -18
  35. package/src/bin/runtime/require.ts +113 -113
  36. package/src/bin/runtime/sql.ts +12 -12
  37. package/src/bin/runtime/switch.ts +60 -60
  38. package/src/index.ts +19 -19
  39. package/src/libs/axios.ts +51 -51
  40. package/src/libs/build.ts +162 -162
  41. package/src/libs/cluster/cluster-controller.ts +93 -93
  42. package/src/libs/cluster.ts +330 -330
  43. package/src/libs/custom.ts +15 -15
  44. package/src/libs/env.ts +10 -10
  45. package/src/libs/fs.ts +399 -399
  46. package/src/libs/hot-reload.ts +63 -63
  47. package/src/libs/models/index.ts +4 -4
  48. package/src/libs/models/model-template-combine.ts +93 -93
  49. package/src/libs/models/model-template-env.ts +58 -58
  50. package/src/libs/models/model-template-union.ts +83 -83
  51. package/src/libs/models/model-template.ts +107 -107
  52. package/src/libs/models/model-validations.ts +443 -443
  53. package/src/libs/models/tools.ts +194 -194
  54. package/src/libs/router.ts +185 -185
  55. package/src/libs/sql.ts +792 -788
  56. package/src/libs/swagger.ts +81 -81
  57. package/src/libs/tools.ts +240 -240
  58. package/src/scripts/post-install/api.ts +32 -32
  59. package/src/scripts/post-install/cluster.ts +28 -28
  60. package/src/scripts/post-install/lib.ts +25 -25
  61. package/src/scripts/post-install.ts +64 -64
  62. package/src/scripts/pre-test.ts +33 -33
  63. package/src/tests/assets/sql/example.sql +76 -76
  64. package/src/tests/config.test.ts +12 -12
  65. package/src/tests/effect.test.ts +62 -62
  66. package/src/tests/model.env.test.ts +66 -66
  67. package/src/tests/model.test.ts +141 -141
  68. package/src/tests/model.union.test.ts +160 -160
  69. package/src/tests/router.test.ts +44 -44
  70. package/src/tests/sql.test.ts +502 -502
  71. package/src/tests/switch.test.ts +76 -76
  72. package/templates/create-cli/class.ts +13 -13
  73. package/templates/create-cli/cluster.ts +14 -14
  74. package/templates/create-cli/cmd.ts +3 -3
  75. package/templates/create-cli/config.ts +2 -2
  76. package/templates/create-cli/lib.ts +3 -3
  77. package/templates/create-cli/model.ts +14 -14
  78. package/templates/create-cli/route.ts +8 -8
  79. package/templates/create-cli/task.ts +13 -13
  80. package/templates/postinstall/api/.vscode/launch.json +20 -20
  81. package/templates/postinstall/api/Dockerfile +24 -24
  82. package/templates/postinstall/api/configs/env.ts +2 -2
  83. package/templates/postinstall/api/envs/debug.env +15 -15
  84. package/templates/postinstall/api/envs/homolog.env +15 -15
  85. package/templates/postinstall/api/envs/prod.env +15 -15
  86. package/templates/postinstall/api/tsconfig.json +24 -24
  87. package/templates/postinstall/cluster/.vscode/launch.json +20 -20
  88. package/templates/postinstall/cluster/Dockerfile +24 -24
  89. package/templates/postinstall/cluster/configs/env.ts +2 -2
  90. package/templates/postinstall/cluster/envs/debug.env +10 -10
  91. package/templates/postinstall/cluster/envs/homolog.env +10 -10
  92. package/templates/postinstall/cluster/envs/prod.env +10 -10
  93. package/templates/postinstall/cluster/tsconfig.json +24 -24
  94. package/templates/postinstall/lib/postinstall-tools.mjs +111 -111
  95. package/templates/postinstall/lib/postinstall.js +33 -33
  96. package/templates/postinstall/lib/tsconfig.json +22 -22
  97. package/templates/swagger-cli/header.json +21 -21
  98. package/templates/swagger-cli/route/body.json +14 -14
  99. package/templates/swagger-cli/route/header.json +18 -18
  100. package/templates/swagger-cli/route/response.json +53 -53
  101. package/tsconfig.json +28 -28
package/src/libs/sql.ts CHANGED
@@ -1,789 +1,793 @@
1
- import knex, { Knex } from "knex"
2
- import { ModelTemplate } from "./models"
3
- import schemaInspector from 'knex-schema-inspector';
4
- import { randomUUID } from "node:crypto"
5
-
6
- export namespace Internals {
7
- export function context<T extends {} | undefined>(c: T): T extends undefined ? void : T {
8
- const context = this.queryContext() ?? Object.create(null)
9
-
10
- if (context._userContext === undefined) {
11
- context._userContext = Object.create(null)
12
- }
13
-
14
- if (c === undefined) {
15
- return context._userContext
16
- }
17
-
18
- context._userContext = c
19
-
20
- return this.queryContext(context)
21
- }
22
-
23
- export function includeOne(table: string, type: Sql.Joins,s: Knex.JoinCallback) {
24
- const t: Knex.QueryBuilder = this
25
- const context = t.queryContext() ?? Object.create(null)
26
-
27
- if (context._joins === undefined) {
28
- context._joins = []
29
- }
30
-
31
- context._joins.push(table)
32
-
33
- const q = t.queryContext(context)
34
-
35
- switch(type) {
36
- case 'CROSS JOIN':
37
- return q.crossJoin(table, s)
38
- break
39
- case 'FULL OUTER JOIN':
40
- return q.fullOuterJoin(table, s)
41
- break
42
- case 'INNER JOIN':
43
- return q.innerJoin(table, s)
44
- break
45
- case 'JOIN':
46
- return q.join(table, s)
47
- break
48
- case 'LEFT JOIN':
49
- return q.leftJoin(table, s)
50
- break
51
- case 'LEFT OUTER JOIN':
52
- return q.leftOuterJoin(table, s)
53
- break
54
- case 'OUTER JOIN':
55
- return q.outerJoin(table, s)
56
- break
57
- case 'RIGHT JOIN':
58
- return q.rightJoin(table, s)
59
- break
60
- }
61
- }
62
-
63
- export function getConnTable(this: Knex.QueryBuilder, table: string, trxName?: string) {
64
- let conn = this.table(table)
65
-
66
- if (trxName !== undefined && conn.comment !== undefined) {
67
- conn = conn.comment(`TRX: ${trxName}`)
68
- }
69
-
70
- return conn
71
- }
72
- }
73
-
74
- export class Driver<InTransaction extends boolean> {
75
- public readonly config: Knex.Config
76
- public readonly inTransaction: InTransaction
77
- public readonly conn: InTransaction extends true ? Knex.Transaction : Knex
78
- public readonly trxConn: InTransaction extends true ? Knex.Transaction : Knex
79
- public readonly cacheTable: Map<string, Sql.CacheTable>
80
- public readonly logQuerys: boolean
81
- public readonly trxName: string | undefined
82
-
83
- constructor(config: Knex.Config, inTransaction: InTransaction, trx?: Knex.Transaction, cacheTable?: Map<string, Sql.CacheTable>) {
84
- this.inTransaction = inTransaction
85
- this.config = config
86
-
87
- this.logQuerys = Plata.config.ENV === 'debug' || Plata.config._PLATA_SQL_SHOWQUERY === '1'
88
-
89
-
90
- if (!this.inTransaction) {
91
- if (config.pool === undefined)
92
- config.pool = {
93
- min: 1,
94
- max: 1
95
- }
96
- ;
97
-
98
- if (!Plata._knexExtended) {
99
- knex.QueryBuilder.extend('context', Internals.context)
100
- knex.QueryBuilder.extend('includeOne', Internals.includeOne)
101
- knex.QueryBuilder.extend('getConnTable', Internals.getConnTable)
102
-
103
- Plata._knexExtended = true
104
- }
105
-
106
- const trxPool = {
107
- min: 1,
108
- max: 1
109
- }
110
-
111
- if (Plata.config.__PLATA_SQL_TRX_MIN_POOL) {
112
- trxPool.min = +Plata.config.__PLATA_SQL_TRX_MIN_POOL
113
- }
114
-
115
- if (Plata.config.__PLATA_SQL_TRX_MAX_POOL) {
116
- trxPool.max = +Plata.config.__PLATA_SQL_TRX_MAX_POOL
117
- }
118
-
119
- this.conn = knex(config) as any
120
- this.trxConn = knex({
121
- ...config,
122
- pool: trxPool
123
- }) as any
124
- } else {
125
- this.conn = trx as any
126
- this.trxConn = trx as any
127
- }
128
-
129
- this.trxName = this.conn?.userParams?.trxName
130
- if (this.logQuerys) {
131
- this.conn.on('query', (...args) => console.log(...args))
132
- this.conn.on('query-error', (...args) => console.error(...args))
133
- }
134
- this.cacheTable = cacheTable ?? new Map()
135
- }
136
-
137
- public runRawWithComment(query: { sql: string, bindings: readonly any[] }) {
138
- if (this.trxName === undefined) {
139
- return this.conn.raw(query.sql, query.bindings)
140
- }
141
-
142
- return this.conn.raw(`/*TRX: ${this.trxName}*/${query.sql}`, query.bindings)
143
- }
144
-
145
- public select<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>) {
146
- return {
147
- build: (builder: Sql.Builder<T>) =>
148
- new Query(
149
- builder(this.conn.queryBuilder().getConnTable(table, this.trxName)),
150
- model,
151
- table,
152
- )
153
- ,
154
- }
155
- }
156
-
157
- public async insert<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>, values: Model.ParcialTemplateType<T>[], options?: Sql.InsertOptions): PlataPromise<Model.ExtractTemplateType<T>[]> {
158
- if (this.config.client === 'mssql') {
159
- const sql = this.conn.queryBuilder().getConnTable(table, this.trxName).insert(
160
- values,
161
- '*',
162
- {
163
- includeTriggerModifications: options?.ignoreTrigger === undefined
164
- }
165
- ).toSQL()
166
-
167
- return this.runRawWithComment(sql).then(
168
- r => r,
169
- err => Plata.BuildPlataError({
170
- errorID: 'PLSQL001',
171
- msg: 'Unexpected error while inserting',
172
- error:{
173
- message: err?.toString(),
174
- stack: new Error().stack
175
- },
176
- })
177
- )
178
- }
179
-
180
- // TODO ver a necessidade de comentario de trx nas outras conexões
181
-
182
- if (options?.ignoreTrigger === true)
183
- return this.conn.queryBuilder().getConnTable(table, this.trxName).insert(
184
- values,
185
- '*',
186
- {
187
- includeTriggerModifications: options?.ignoreTrigger
188
- }
189
- ).then(
190
- r => r,
191
- err => Plata.BuildPlataError({
192
- errorID: 'PLSQLINS001',
193
- msg: 'Unexpected error while inserting',
194
- error: {
195
- message: err?.toString(),
196
- stack: err?.stack ?? new Error().stack
197
- },
198
- })
199
- )
200
- ;
201
-
202
- const key: PlataResult<string> = await schemaInspector(this.conn).primary(table).then(
203
- r => {
204
- if (r === null) {
205
- return Plata.BuildPlataError({
206
- errorID: 'PLSQL007',
207
- msg: 'Tabela não tem chave primária'
208
- })
209
- }
210
-
211
- return r
212
- },
213
- err => Plata.BuildPlataError({
214
- errorID: 'PLSQL006',
215
- msg: 'Erro ao consultar a chave primária da tabela',
216
- error: {
217
- message: err?.toString(),
218
- stack: err?.stack ?? new Error().stack
219
- }
220
- })
221
- )
222
-
223
- if (key.errorID !== undefined) {
224
- return key
225
- }
226
-
227
- return this.transaction(
228
- async (trx) => {
229
- const result: number[] = await trx.conn.queryBuilder().getConnTable(table, `${trx.trxName}<->${this.trxName}`).insert(
230
- values,
231
- )
232
-
233
- return trx.conn.select('*').from(table).whereIn(key, result)
234
- }
235
- )
236
- }
237
-
238
- public update<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>) {
239
- return {
240
- build: async (s: Sql.Builder<T>, set: Model.ParcialTemplateType<T>): PlataPromise<true> => {
241
- const sql = s(this.conn.queryBuilder().getConnTable(table, this.trxName)).update(set as any).toSQL()
242
-
243
- return this.runRawWithComment(sql).then(
244
- () => true,
245
- err => Plata.BuildPlataError({
246
- errorID: 'PLSQL002',
247
- msg: 'Unexpected error while update',
248
- error: {
249
- message: err?.toString(),
250
- stack: err?.stack ?? new Error().stack
251
- }
252
- })
253
- )
254
- },
255
- }
256
- }
257
-
258
- public log(text: string, ...extras: any[]) {
259
- if (this.logQuerys === true) {
260
- console.log(text, ...extras)
261
- }
262
- }
263
-
264
- /** @deprecated */
265
- public trasation<T = any>(transaction: Sql.TransactionFunction<Driver<true>, T>, config?: Knex.TransactionConfig) {
266
- return this.conn.transaction(async trx => {
267
- const transactionDriver = new Driver(this.config, true, trx, this.cacheTable)
268
-
269
- const result = await transaction(transactionDriver)
270
-
271
- return result
272
- }, config).then(
273
- r => r,
274
- err => {
275
- if (!err) {
276
- err = Object.create(null)
277
- }
278
-
279
- err.originalError = undefined
280
-
281
- return Plata.BuildPlataError({
282
- errorID: 'PLSQL003',
283
- msg: 'Unexpected error in transation',
284
- error: {
285
- message: err?.toString(),
286
- stack: err?.stack ?? new Error().stack
287
- },
288
- })
289
- }
290
- )
291
- }
292
-
293
- public async getTransactionDriver(config?: Knex.TransactionConfig): PlataPromise<Driver<true>> {
294
- const trxName = randomUUID()
295
- const provider = await this.trxConn.transactionProvider({
296
- ...config,
297
- doNotRejectOnRollback: true,
298
- })
299
-
300
- this.log(`[PLATA-SQL] [TRX-${trxName}] - Inciando provider`)
301
-
302
- // Inicia a transação
303
- const trx: PlataResult<Knex.Transaction> = await provider().catch(
304
- e => ({
305
- errorID: 'PLSQLGETTRX001',
306
- msg: 'Erro ao iniciar a transação',
307
- error: {
308
- message: e?.toString(),
309
- stack: e?.stack ?? new Error().stack
310
- }
311
- })
312
- )
313
-
314
- if (trx.errorID !== undefined) {
315
- this.log(`[PLATA-SQL] [TRX-${trxName}] - Conexão não foi aberta`)
316
- return trx
317
- }
318
-
319
- trx.userParams.trxName = trxName
320
-
321
- this.log(`[PLATA-SQL] [TRX-${trxName}] - Conexão foi aberta`)
322
- return new Driver(this.config, true, trx, this.cacheTable)
323
- }
324
-
325
- public async transaction<T extends Sql.TransactionReturn>(transaction: Sql.TransactionResultFunction<Driver<true>, T>, config?: Knex.TransactionConfig): PlataPromise<T> {
326
- const driver = await this.getTransactionDriver(config)
327
-
328
- if (driver.errorID !== undefined) {
329
- return driver
330
- }
331
-
332
- const transPromise = Plata.FastPromise(() => driver.conn.executionPromise.catch((...args) => console.log(...args)))
333
-
334
- try {
335
- const resultTransation = await transaction(driver)
336
-
337
- if (resultTransation.errorID !== undefined) {
338
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback -> Erro da API na transação`)
339
- await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
340
- await transPromise
341
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback realizado`)
342
-
343
- return resultTransation
344
- }
345
-
346
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Commit -> Realizando Commit`)
347
- const resultCommit: PlataResult<true> = await driver.conn.commit().then(
348
- () => true,
349
- err => ({
350
- errorID: 'PLSQLTRX003',
351
- msg: 'Erro inesperado ao realizar o commit',
352
- error: {
353
- message: err?.toString(),
354
- stack: err?.stack ?? new Error().stack
355
- }
356
- })
357
- )
358
-
359
-
360
- if (resultCommit.errorID !== undefined) {
361
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Erro no commit, realizando Rollback`)
362
- await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
363
- await transPromise
364
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback realizado`)
365
- return resultCommit
366
- }
367
-
368
- await transPromise
369
-
370
- return resultTransation
371
- } catch (err) {
372
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Erro inesperado: `, err)
373
- await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
374
- await transPromise
375
- this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Roollback realizado`)
376
-
377
- return {
378
- errorID: 'PLSQLTRX002',
379
- msg: 'Erro inesperado ao realizar a transação ',
380
- error: {
381
- message: err?.toString(),
382
- stack: err?.stack ?? new Error().stack
383
- }
384
- }
385
- }
386
- }
387
-
388
- public async getColumns(table: string): PlataPromise<Record<string | number | symbol, Knex.ColumnInfo>> {
389
- if (Plata.config._PLATA_SQL_CACHETABLE !== '1')
390
- return this.getColumsBanco(table)
391
- ;
392
-
393
- const cache = this.cacheTable.get(table)
394
-
395
- if (cache === undefined) {
396
- const result = await this.getColumsBanco(table)
397
-
398
- if (result.errorID !== undefined) {
399
- return result
400
- }
401
-
402
- this.cacheTable.set(table, {
403
- coluns: result,
404
- date: new Date()
405
- })
406
-
407
- return result
408
- }
409
-
410
- const ttl = (+(Plata.config._PLATA_SQL_CACHETABLETIME || 60) || 60) * 60 * 1000
411
-
412
- if ((new Date().getTime() - cache.date.getTime()) > ttl) {
413
- const result = await this.getColumsBanco(table)
414
-
415
- if (result.errorID !== undefined) {
416
- return result
417
- }
418
-
419
- this.cacheTable.set(table, {
420
- coluns: result,
421
- date: new Date()
422
- })
423
-
424
- return result
425
- }
426
-
427
- return cache.coluns
428
- }
429
-
430
- public async getColumsBanco(table: string): PlataPromise<Record<string | number | symbol, Knex.ColumnInfo>> {
431
- return this.conn(table).columnInfo().catch(
432
- err => Plata.BuildPlataError({
433
- errorID: 'PLSQL004',
434
- msg: 'Erro ao consultar a estrutura da tabela',
435
- error: {
436
- message: err?.toString(),
437
- stack: err?.stack ?? new Error().stack
438
- }
439
- })
440
- )
441
- }
442
-
443
- private getDefaultValue(type: string, value: any | null): any {
444
- if (value !== null) {
445
- return value
446
- }
447
-
448
- switch (type) {
449
- case 'image':
450
- return ''
451
- case 'int':
452
- case 'integer':
453
- return 0
454
- case 'datetime2':
455
- return null
456
- case 'decimal':
457
- return 0
458
- case 'varbinary':
459
- return ''
460
- case 'text':
461
- return ''
462
- case 'smallint':
463
- return 0
464
- case 'datetimeoffset':
465
- return 0
466
- case 'varchar':
467
- return ''
468
- case 'binary':
469
- return 0
470
- case 'datetime':
471
- return null
472
- case 'time':
473
- return null
474
- case 'numeric':
475
- return 0
476
- case 'uniqueidentifier':
477
- return 0
478
- case 'tinyint':
479
- return 0
480
- case 'nchar':
481
- return ''
482
- case 'smalldatetime':
483
- return null
484
- case 'float':
485
- return 0
486
- case 'date':
487
- return null
488
- case 'char':
489
- return ''
490
- case 'bigint':
491
- return 0
492
- case 'nvarchar':
493
- return ''
494
- case 'sysname':
495
- return ''
496
- case 'bit':
497
- return 0
498
- default:
499
- return ''
500
- }
501
- }
502
-
503
- public async autoInsert<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>, values: (Model.ParcialTemplateType<T>)[], ignore: StringKeyOfObject<T>[], options?: Sql.InsertOptions): PlataPromise<Model.ExtractTemplateType<T>[]> {
504
- const columns = await this.getColumns(table)
505
-
506
- if (columns.errorID !== undefined) {
507
- return columns
508
- }
509
-
510
- const newValues: any[] = []
511
-
512
- for (let i = 0; i < values.length; i++) {
513
- const v = Object.create(null)
514
-
515
- for (const key in values[i]) {
516
- v[key.toUpperCase()] = values[i][key]
517
- }
518
-
519
- const insertValues = Object.create(null)
520
-
521
- for (const key in columns) {
522
- const n = key.toUpperCase()
523
-
524
- if (ignore.find(c => c.toString().toUpperCase() === n) !== undefined) {
525
- continue
526
- }
527
-
528
- const value = this.getDefaultValue(
529
- columns[key].type.toLowerCase(),
530
- v[n] ?? null
531
- )
532
-
533
- if (value === null) {
534
- continue
535
- }
536
-
537
- insertValues[n] = value
538
- }
539
-
540
- newValues.push(insertValues)
541
- }
542
-
543
- const inserted: PlataResult<any[]> = await this.insert(
544
- table,
545
- {} as any,
546
- newValues,
547
-
548
- )
549
-
550
- if (inserted.errorID !== undefined) {
551
- return inserted
552
- }
553
-
554
- const insertedRows: any[] = []
555
-
556
- for (let i = 0; i < inserted.length; i++) {
557
- const item = inserted[i];
558
-
559
- const insertedRow = Object.create(null)
560
-
561
- for (const key in item) {
562
- insertedRow[key.toUpperCase()] = item[key]
563
- }
564
-
565
- insertedRows.push(insertedRow)
566
- }
567
-
568
- return insertedRows
569
- }
570
- }
571
-
572
- export class Query<T extends Model.Template, C extends Model.Converters<T>> {
573
- public query: Sql.ModelQueryBuilder<T>
574
- public readonly model: ModelTemplate<T, C>
575
- public readonly table: string
576
- public readonly joins: string[]
577
-
578
- constructor (query: Sql.ModelQueryBuilder<T>, model: ModelTemplate<T, C>, table: string) {
579
- const context = query.queryContext() ?? Object.create(null)
580
-
581
- this.joins = context._joins ?? []
582
-
583
- this.table = table
584
- this.model = model
585
- this.query = this._buildSelect(query, this.model.template)
586
- }
587
-
588
- private _buildSelect<T extends Model.Template>(query: Sql.ModelQueryBuilder<T>, template: T): Sql.ModelQueryBuilder<T> {
589
- const cs: string[] = []
590
-
591
- for (const key in template) {
592
- if (!Array.isArray(template[key])) {
593
- continue
594
- }
595
-
596
- if (Array.isArray(template[key][0])) {
597
- continue
598
- }
599
-
600
- if (typeof template[key][0] === 'function') {
601
- cs.push(`${this.table}.${key} as ${this.table}_${key}`)
602
- continue
603
- }
604
-
605
- if (!this.joins.includes(key)) {
606
- continue
607
- }
608
-
609
- for (const column in template[key][0]) {
610
- cs.push(`${key}.${column} as ${key}_${column}`)
611
- }
612
- }
613
-
614
- return query.select(...cs)
615
- }
616
-
617
- private _buildModel<T extends Model.Template>(row: any): typeof this.model.type {
618
- const template = this.model.template
619
- const r: typeof this.model.type = Object.create(null) as any
620
-
621
- for (const key in template) {
622
- if (!Array.isArray(template[key])) {
623
- continue
624
- }
625
-
626
- if (Array.isArray(template[key][0])) {
627
- r[key] = [] as any
628
- continue
629
- }
630
-
631
- if (typeof template[key][0] === 'function') {
632
- r[key] = row[`${this.table}_${key}`] ?? null
633
- continue
634
- }
635
-
636
- if (!this.joins.includes(key)) {
637
- r[key] = [] as any
638
- continue
639
- }
640
-
641
- r[key] = [Object.create(null)] as any
642
-
643
- for (const column in template[key][0]) {
644
- r[key][0][column] = row[`${key}_${column}`] ?? null
645
- }
646
- }
647
-
648
- return r
649
- }
650
-
651
- public _handleStream<Y>(handler: Sql.StreamHandler): PlataPromise<Y> {
652
- return new Promise(resolve => {
653
- let shoudContinue = true
654
-
655
- const stream = this.query.stream()
656
-
657
- stream.on('close', async () => {
658
- return resolve(handler.close<Y>())
659
- })
660
-
661
- stream.on('data', async row => {
662
- if (shoudContinue) {
663
- shoudContinue = await handler.row(this._buildModel(row))
664
- } else {
665
- stream.pause()
666
- }
667
- })
668
-
669
- stream.on('pause', () => {
670
- stream.destroy()
671
- })
672
-
673
- stream.on('error', async err => {
674
- shoudContinue = false
675
-
676
- if (handler.error !== undefined) await handler.error(err)
677
-
678
- return resolve({
679
- errorID: 'PLSQLQ001',
680
- msg: 'Unexpected error in query',
681
- error: {
682
- message: err?.toString(),
683
- stack: err?.stack ?? new Error().stack
684
- }
685
- })
686
- })
687
- })
688
- }
689
-
690
- public async toListAsync(): Sql.ModelList<T> {
691
- const errors: PlataError[] = []
692
- const itens: any[] = []
693
-
694
- const result: PlataResult<boolean[]> = await this.query.then(
695
- r => {
696
- const promises: Promise<boolean>[] = []
697
-
698
- for (let i = 0; i < r.length; i++) {
699
- promises.push(Plata.FastPromise(async () => {
700
- const v = await this.model.validate(this._buildModel(r[i]))
701
-
702
- if (v.errors !== undefined) {
703
- errors.push(...v.errors)
704
- }
705
-
706
- itens.push(v.value)
707
-
708
- return true
709
- }))
710
- }
711
-
712
- return Promise.all(promises)
713
- },
714
- err => Plata.BuildPlataError({
715
- errorID: 'PLSQLQ005',
716
- msg: 'Erro ao execultar a query',
717
- error: {
718
- message: err?.toString(),
719
- stack: err?.stack ?? new Error().stack
720
- }
721
- })
722
- )
723
-
724
- if (result.errorID !== undefined) {
725
- return result
726
- }
727
-
728
- if (errors.length !== 0) {
729
- return {
730
- errors,
731
- itens,
732
- }
733
- }
734
-
735
- return {
736
- errors: undefined,
737
- itens,
738
- }
739
- }
740
-
741
- public async eachRowAsync(c: Sql.EachRowCallBack<T>): PlataPromise<boolean> {
742
- const promises: Promise<void>[] = []
743
- let shoudContinue = true
744
- let error: PlataError | undefined = undefined
745
-
746
- return this._handleStream<MaybePlataError>({
747
- row: async row => {
748
- promises.push(Plata.FastPromise(() => this.model.validate(row))
749
- .then(
750
- lineResult => {
751
- c(lineResult).then(r => {
752
- if (r.errorID !== undefined) {
753
- shoudContinue = false
754
- error = r
755
- return
756
- }
757
-
758
- shoudContinue = shoudContinue ? r : shoudContinue
759
- })
760
- }
761
- )
762
- .catch(err => {
763
- shoudContinue = false
764
- error = {
765
- errorID: 'PLSQLQ003',
766
- msg: 'Unexpected error in callback',
767
- error: {
768
- message: err?.toString(),
769
- stack: err?.stack ?? new Error().stack
770
- }
771
- }
772
- })
773
- )
774
-
775
- return shoudContinue
776
- },
777
- close: async (): PlataPromise<boolean> => {
778
- return Promise.all(promises).then(
779
- () => error ?? true,
780
- err => Plata.BuildPlataError({
781
- errorID: 'PLSQLQ004',
782
- msg: 'Unexpected Error in select',
783
- error: err
784
- })
785
- )
786
- }
787
- })
788
- }
1
+ import knex, { Knex } from "knex"
2
+ import { ModelTemplate } from "./models"
3
+ import schemaInspector from 'knex-schema-inspector';
4
+ import { randomUUID } from "node:crypto"
5
+
6
+ export namespace Internals {
7
+ export function context<T extends {} | undefined>(c: T): T extends undefined ? void : T {
8
+ const context = this.queryContext() ?? Object.create(null)
9
+
10
+ if (context._userContext === undefined) {
11
+ context._userContext = Object.create(null)
12
+ }
13
+
14
+ if (c === undefined) {
15
+ return context._userContext
16
+ }
17
+
18
+ context._userContext = c
19
+
20
+ return this.queryContext(context)
21
+ }
22
+
23
+ export function includeOne(table: string, type: Sql.Joins,s: Knex.JoinCallback) {
24
+ const t: Knex.QueryBuilder = this
25
+ const context = t.queryContext() ?? Object.create(null)
26
+
27
+ if (context._joins === undefined) {
28
+ context._joins = []
29
+ }
30
+
31
+ context._joins.push(table)
32
+
33
+ const q = t.queryContext(context)
34
+
35
+ switch(type) {
36
+ case 'CROSS JOIN':
37
+ return q.crossJoin(table, s)
38
+ break
39
+ case 'FULL OUTER JOIN':
40
+ return q.fullOuterJoin(table, s)
41
+ break
42
+ case 'INNER JOIN':
43
+ return q.innerJoin(table, s)
44
+ break
45
+ case 'JOIN':
46
+ return q.join(table, s)
47
+ break
48
+ case 'LEFT JOIN':
49
+ return q.leftJoin(table, s)
50
+ break
51
+ case 'LEFT OUTER JOIN':
52
+ return q.leftOuterJoin(table, s)
53
+ break
54
+ case 'OUTER JOIN':
55
+ return q.outerJoin(table, s)
56
+ break
57
+ case 'RIGHT JOIN':
58
+ return q.rightJoin(table, s)
59
+ break
60
+ }
61
+ }
62
+
63
+ export function getConnTable(this: Knex.QueryBuilder, table: string, trxName?: string) {
64
+ let conn = this.table(table)
65
+
66
+ if (trxName !== undefined && conn.comment !== undefined) {
67
+ conn = conn.comment(`TRX: ${trxName}`)
68
+ }
69
+
70
+ return conn
71
+ }
72
+ }
73
+
74
+ export class Driver<InTransaction extends boolean> {
75
+ public readonly config: Knex.Config
76
+ public readonly inTransaction: InTransaction
77
+ public readonly conn: InTransaction extends true ? Knex.Transaction : Knex
78
+ public readonly trxConn: InTransaction extends true ? Knex.Transaction : Knex
79
+ public readonly cacheTable: Map<string, Sql.CacheTable>
80
+ public readonly logQuerys: boolean
81
+ public readonly trxName: string | undefined
82
+
83
+ constructor(config: Knex.Config, inTransaction: InTransaction, trx?: Knex.Transaction, cacheTable?: Map<string, Sql.CacheTable>) {
84
+ this.inTransaction = inTransaction
85
+ this.config = config
86
+
87
+ this.logQuerys = Plata.config.ENV === 'debug' || Plata.config._PLATA_SQL_SHOWQUERY === '1'
88
+
89
+
90
+ if (!this.inTransaction) {
91
+ if (config.pool === undefined)
92
+ config.pool = {
93
+ min: 1,
94
+ max: 1
95
+ }
96
+ ;
97
+
98
+ if (!Plata._knexExtended) {
99
+ knex.QueryBuilder.extend('context', Internals.context)
100
+ knex.QueryBuilder.extend('includeOne', Internals.includeOne)
101
+ knex.QueryBuilder.extend('getConnTable', Internals.getConnTable)
102
+
103
+ Plata._knexExtended = true
104
+ }
105
+
106
+ const trxPool = {
107
+ min: 1,
108
+ max: 1
109
+ }
110
+
111
+ if (Plata.config.__PLATA_SQL_TRX_MIN_POOL) {
112
+ trxPool.min = +Plata.config.__PLATA_SQL_TRX_MIN_POOL
113
+ }
114
+
115
+ if (Plata.config.__PLATA_SQL_TRX_MAX_POOL) {
116
+ trxPool.max = +Plata.config.__PLATA_SQL_TRX_MAX_POOL
117
+ }
118
+
119
+ this.conn = knex(config) as any
120
+ this.trxConn = knex({
121
+ ...config,
122
+ pool: trxPool
123
+ }) as any
124
+ } else {
125
+ this.conn = trx as any
126
+ this.trxConn = trx as any
127
+ }
128
+
129
+ this.trxName = this.conn?.userParams?.trxName
130
+ if (this.logQuerys) {
131
+ this.conn.on('query', (...args) => console.log(...args))
132
+ this.conn.on('query-error', (...args) => console.error(...args))
133
+ }
134
+ this.cacheTable = cacheTable ?? new Map()
135
+ }
136
+
137
+ public async onOpenTrx(trx: Driver<true>): PlataPromise<Driver<true>> {
138
+ return trx
139
+ }
140
+
141
+ public runRawWithComment(query: { sql: string, bindings: readonly any[] }) {
142
+ if (this.trxName === undefined) {
143
+ return this.conn.raw(query.sql, query.bindings)
144
+ }
145
+
146
+ return this.conn.raw(`/*TRX: ${this.trxName}*/${query.sql}`, query.bindings)
147
+ }
148
+
149
+ public select<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>) {
150
+ return {
151
+ build: (builder: Sql.Builder<T>) =>
152
+ new Query(
153
+ builder(this.conn.queryBuilder().getConnTable(table, this.trxName)),
154
+ model,
155
+ table,
156
+ )
157
+ ,
158
+ }
159
+ }
160
+
161
+ public async insert<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>, values: Model.ParcialTemplateType<T>[], options?: Sql.InsertOptions): PlataPromise<Model.ExtractTemplateType<T>[]> {
162
+ if (this.config.client === 'mssql') {
163
+ const sql = this.conn.queryBuilder().getConnTable(table, this.trxName).insert(
164
+ values,
165
+ '*',
166
+ {
167
+ includeTriggerModifications: options?.ignoreTrigger === undefined
168
+ }
169
+ ).toSQL()
170
+
171
+ return this.runRawWithComment(sql).then(
172
+ r => r,
173
+ err => Plata.BuildPlataError({
174
+ errorID: 'PLSQL001',
175
+ msg: 'Unexpected error while inserting',
176
+ error:{
177
+ message: err?.toString(),
178
+ stack: new Error().stack
179
+ },
180
+ })
181
+ )
182
+ }
183
+
184
+ // TODO ver a necessidade de comentario de trx nas outras conexões
185
+
186
+ if (options?.ignoreTrigger === true)
187
+ return this.conn.queryBuilder().getConnTable(table, this.trxName).insert(
188
+ values,
189
+ '*',
190
+ {
191
+ includeTriggerModifications: options?.ignoreTrigger
192
+ }
193
+ ).then(
194
+ r => r,
195
+ err => Plata.BuildPlataError({
196
+ errorID: 'PLSQLINS001',
197
+ msg: 'Unexpected error while inserting',
198
+ error: {
199
+ message: err?.toString(),
200
+ stack: err?.stack ?? new Error().stack
201
+ },
202
+ })
203
+ )
204
+ ;
205
+
206
+ const key: PlataResult<string> = await schemaInspector(this.conn).primary(table).then(
207
+ r => {
208
+ if (r === null) {
209
+ return Plata.BuildPlataError({
210
+ errorID: 'PLSQL007',
211
+ msg: 'Tabela não tem chave primária'
212
+ })
213
+ }
214
+
215
+ return r
216
+ },
217
+ err => Plata.BuildPlataError({
218
+ errorID: 'PLSQL006',
219
+ msg: 'Erro ao consultar a chave primária da tabela',
220
+ error: {
221
+ message: err?.toString(),
222
+ stack: err?.stack ?? new Error().stack
223
+ }
224
+ })
225
+ )
226
+
227
+ if (key.errorID !== undefined) {
228
+ return key
229
+ }
230
+
231
+ return this.transaction(
232
+ async (trx) => {
233
+ const result: number[] = await trx.conn.queryBuilder().getConnTable(table, `${trx.trxName}<->${this.trxName}`).insert(
234
+ values,
235
+ )
236
+
237
+ return trx.conn.select('*').from(table).whereIn(key, result)
238
+ }
239
+ )
240
+ }
241
+
242
+ public update<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>) {
243
+ return {
244
+ build: async (s: Sql.Builder<T>, set: Model.ParcialTemplateType<T>): PlataPromise<true> => {
245
+ const sql = s(this.conn.queryBuilder().getConnTable(table, this.trxName)).update(set as any).toSQL()
246
+
247
+ return this.runRawWithComment(sql).then(
248
+ () => true,
249
+ err => Plata.BuildPlataError({
250
+ errorID: 'PLSQL002',
251
+ msg: 'Unexpected error while update',
252
+ error: {
253
+ message: err?.toString(),
254
+ stack: err?.stack ?? new Error().stack
255
+ }
256
+ })
257
+ )
258
+ },
259
+ }
260
+ }
261
+
262
+ public log(text: string, ...extras: any[]) {
263
+ if (this.logQuerys === true) {
264
+ console.log(text, ...extras)
265
+ }
266
+ }
267
+
268
+ /** @deprecated */
269
+ public trasation<T = any>(transaction: Sql.TransactionFunction<Driver<true>, T>, config?: Knex.TransactionConfig) {
270
+ return this.conn.transaction(async trx => {
271
+ const transactionDriver = new Driver(this.config, true, trx, this.cacheTable)
272
+
273
+ const result = await transaction(transactionDriver)
274
+
275
+ return result
276
+ }, config).then(
277
+ r => r,
278
+ err => {
279
+ if (!err) {
280
+ err = Object.create(null)
281
+ }
282
+
283
+ err.originalError = undefined
284
+
285
+ return Plata.BuildPlataError({
286
+ errorID: 'PLSQL003',
287
+ msg: 'Unexpected error in transation',
288
+ error: {
289
+ message: err?.toString(),
290
+ stack: err?.stack ?? new Error().stack
291
+ },
292
+ })
293
+ }
294
+ )
295
+ }
296
+
297
+ public async getTransactionDriver(config?: Knex.TransactionConfig): PlataPromise<Driver<true>> {
298
+ const trxName = randomUUID()
299
+ const provider = await this.trxConn.transactionProvider({
300
+ ...config,
301
+ doNotRejectOnRollback: true,
302
+ })
303
+
304
+ this.log(`[PLATA-SQL] [TRX-${trxName}] - Inciando provider`)
305
+
306
+ // Inicia a transação
307
+ const trx: PlataResult<Knex.Transaction> = await provider().catch(
308
+ e => ({
309
+ errorID: 'PLSQLGETTRX001',
310
+ msg: 'Erro ao iniciar a transação',
311
+ error: {
312
+ message: e?.toString(),
313
+ stack: e?.stack ?? new Error().stack
314
+ }
315
+ })
316
+ )
317
+
318
+ if (trx.errorID !== undefined) {
319
+ this.log(`[PLATA-SQL] [TRX-${trxName}] - Conexão não foi aberta`)
320
+ return trx
321
+ }
322
+
323
+ trx.userParams.trxName = trxName
324
+
325
+ this.log(`[PLATA-SQL] [TRX-${trxName}] - Conexão foi aberta`)
326
+ return this.onOpenTrx(new Driver(this.config, true, trx, this.cacheTable))
327
+ }
328
+
329
+ public async transaction<T extends Sql.TransactionReturn>(transaction: Sql.TransactionResultFunction<Driver<true>, T>, config?: Knex.TransactionConfig): PlataPromise<T> {
330
+ const driver = await this.getTransactionDriver(config)
331
+
332
+ if (driver.errorID !== undefined) {
333
+ return driver
334
+ }
335
+
336
+ const transPromise = Plata.FastPromise(() => driver.conn.executionPromise.catch((...args) => console.log(...args)))
337
+
338
+ try {
339
+ const resultTransation = await transaction(driver)
340
+
341
+ if (resultTransation.errorID !== undefined) {
342
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback -> Erro da API na transação`)
343
+ await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
344
+ await transPromise
345
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback realizado`)
346
+
347
+ return resultTransation
348
+ }
349
+
350
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Commit -> Realizando Commit`)
351
+ const resultCommit: PlataResult<true> = await driver.conn.commit().then(
352
+ () => true,
353
+ err => ({
354
+ errorID: 'PLSQLTRX003',
355
+ msg: 'Erro inesperado ao realizar o commit',
356
+ error: {
357
+ message: err?.toString(),
358
+ stack: err?.stack ?? new Error().stack
359
+ }
360
+ })
361
+ )
362
+
363
+
364
+ if (resultCommit.errorID !== undefined) {
365
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Erro no commit, realizando Rollback`)
366
+ await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
367
+ await transPromise
368
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Roollback realizado`)
369
+ return resultCommit
370
+ }
371
+
372
+ await transPromise
373
+
374
+ return resultTransation
375
+ } catch (err) {
376
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Erro inesperado: `, err)
377
+ await driver.conn.rollback().catch((...err) => console.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}]`, ...err))
378
+ await transPromise
379
+ this.log(`[PLATA-SQL] [TRX-${driver.conn.userParams?.trxName}] - Rollback -> Roollback realizado`)
380
+
381
+ return {
382
+ errorID: 'PLSQLTRX002',
383
+ msg: 'Erro inesperado ao realizar a transação ',
384
+ error: {
385
+ message: err?.toString(),
386
+ stack: err?.stack ?? new Error().stack
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ public async getColumns(table: string): PlataPromise<Record<string | number | symbol, Knex.ColumnInfo>> {
393
+ if (Plata.config._PLATA_SQL_CACHETABLE !== '1')
394
+ return this.getColumsBanco(table)
395
+ ;
396
+
397
+ const cache = this.cacheTable.get(table)
398
+
399
+ if (cache === undefined) {
400
+ const result = await this.getColumsBanco(table)
401
+
402
+ if (result.errorID !== undefined) {
403
+ return result
404
+ }
405
+
406
+ this.cacheTable.set(table, {
407
+ coluns: result,
408
+ date: new Date()
409
+ })
410
+
411
+ return result
412
+ }
413
+
414
+ const ttl = (+(Plata.config._PLATA_SQL_CACHETABLETIME || 60) || 60) * 60 * 1000
415
+
416
+ if ((new Date().getTime() - cache.date.getTime()) > ttl) {
417
+ const result = await this.getColumsBanco(table)
418
+
419
+ if (result.errorID !== undefined) {
420
+ return result
421
+ }
422
+
423
+ this.cacheTable.set(table, {
424
+ coluns: result,
425
+ date: new Date()
426
+ })
427
+
428
+ return result
429
+ }
430
+
431
+ return cache.coluns
432
+ }
433
+
434
+ public async getColumsBanco(table: string): PlataPromise<Record<string | number | symbol, Knex.ColumnInfo>> {
435
+ return this.conn(table).columnInfo().catch(
436
+ err => Plata.BuildPlataError({
437
+ errorID: 'PLSQL004',
438
+ msg: 'Erro ao consultar a estrutura da tabela',
439
+ error: {
440
+ message: err?.toString(),
441
+ stack: err?.stack ?? new Error().stack
442
+ }
443
+ })
444
+ )
445
+ }
446
+
447
+ private getDefaultValue(type: string, value: any | null): any {
448
+ if (value !== null) {
449
+ return value
450
+ }
451
+
452
+ switch (type) {
453
+ case 'image':
454
+ return ''
455
+ case 'int':
456
+ case 'integer':
457
+ return 0
458
+ case 'datetime2':
459
+ return null
460
+ case 'decimal':
461
+ return 0
462
+ case 'varbinary':
463
+ return ''
464
+ case 'text':
465
+ return ''
466
+ case 'smallint':
467
+ return 0
468
+ case 'datetimeoffset':
469
+ return 0
470
+ case 'varchar':
471
+ return ''
472
+ case 'binary':
473
+ return 0
474
+ case 'datetime':
475
+ return null
476
+ case 'time':
477
+ return null
478
+ case 'numeric':
479
+ return 0
480
+ case 'uniqueidentifier':
481
+ return 0
482
+ case 'tinyint':
483
+ return 0
484
+ case 'nchar':
485
+ return ''
486
+ case 'smalldatetime':
487
+ return null
488
+ case 'float':
489
+ return 0
490
+ case 'date':
491
+ return null
492
+ case 'char':
493
+ return ''
494
+ case 'bigint':
495
+ return 0
496
+ case 'nvarchar':
497
+ return ''
498
+ case 'sysname':
499
+ return ''
500
+ case 'bit':
501
+ return 0
502
+ default:
503
+ return ''
504
+ }
505
+ }
506
+
507
+ public async autoInsert<T extends Model.Template, C extends Model.Converters<T>>(table: string, model: ModelTemplate<T, C>, values: (Model.ParcialTemplateType<T>)[], ignore: StringKeyOfObject<T>[], options?: Sql.InsertOptions): PlataPromise<Model.ExtractTemplateType<T>[]> {
508
+ const columns = await this.getColumns(table)
509
+
510
+ if (columns.errorID !== undefined) {
511
+ return columns
512
+ }
513
+
514
+ const newValues: any[] = []
515
+
516
+ for (let i = 0; i < values.length; i++) {
517
+ const v = Object.create(null)
518
+
519
+ for (const key in values[i]) {
520
+ v[key.toUpperCase()] = values[i][key]
521
+ }
522
+
523
+ const insertValues = Object.create(null)
524
+
525
+ for (const key in columns) {
526
+ const n = key.toUpperCase()
527
+
528
+ if (ignore.find(c => c.toString().toUpperCase() === n) !== undefined) {
529
+ continue
530
+ }
531
+
532
+ const value = this.getDefaultValue(
533
+ columns[key].type.toLowerCase(),
534
+ v[n] ?? null
535
+ )
536
+
537
+ if (value === null) {
538
+ continue
539
+ }
540
+
541
+ insertValues[n] = value
542
+ }
543
+
544
+ newValues.push(insertValues)
545
+ }
546
+
547
+ const inserted: PlataResult<any[]> = await this.insert(
548
+ table,
549
+ {} as any,
550
+ newValues,
551
+
552
+ )
553
+
554
+ if (inserted.errorID !== undefined) {
555
+ return inserted
556
+ }
557
+
558
+ const insertedRows: any[] = []
559
+
560
+ for (let i = 0; i < inserted.length; i++) {
561
+ const item = inserted[i];
562
+
563
+ const insertedRow = Object.create(null)
564
+
565
+ for (const key in item) {
566
+ insertedRow[key.toUpperCase()] = item[key]
567
+ }
568
+
569
+ insertedRows.push(insertedRow)
570
+ }
571
+
572
+ return insertedRows
573
+ }
574
+ }
575
+
576
+ export class Query<T extends Model.Template, C extends Model.Converters<T>> {
577
+ public query: Sql.ModelQueryBuilder<T>
578
+ public readonly model: ModelTemplate<T, C>
579
+ public readonly table: string
580
+ public readonly joins: string[]
581
+
582
+ constructor (query: Sql.ModelQueryBuilder<T>, model: ModelTemplate<T, C>, table: string) {
583
+ const context = query.queryContext() ?? Object.create(null)
584
+
585
+ this.joins = context._joins ?? []
586
+
587
+ this.table = table
588
+ this.model = model
589
+ this.query = this._buildSelect(query, this.model.template)
590
+ }
591
+
592
+ private _buildSelect<T extends Model.Template>(query: Sql.ModelQueryBuilder<T>, template: T): Sql.ModelQueryBuilder<T> {
593
+ const cs: string[] = []
594
+
595
+ for (const key in template) {
596
+ if (!Array.isArray(template[key])) {
597
+ continue
598
+ }
599
+
600
+ if (Array.isArray(template[key][0])) {
601
+ continue
602
+ }
603
+
604
+ if (typeof template[key][0] === 'function') {
605
+ cs.push(`${this.table}.${key} as ${this.table}_${key}`)
606
+ continue
607
+ }
608
+
609
+ if (!this.joins.includes(key)) {
610
+ continue
611
+ }
612
+
613
+ for (const column in template[key][0]) {
614
+ cs.push(`${key}.${column} as ${key}_${column}`)
615
+ }
616
+ }
617
+
618
+ return query.select(...cs)
619
+ }
620
+
621
+ private _buildModel<T extends Model.Template>(row: any): typeof this.model.type {
622
+ const template = this.model.template
623
+ const r: typeof this.model.type = Object.create(null) as any
624
+
625
+ for (const key in template) {
626
+ if (!Array.isArray(template[key])) {
627
+ continue
628
+ }
629
+
630
+ if (Array.isArray(template[key][0])) {
631
+ r[key] = [] as any
632
+ continue
633
+ }
634
+
635
+ if (typeof template[key][0] === 'function') {
636
+ r[key] = row[`${this.table}_${key}`] ?? null
637
+ continue
638
+ }
639
+
640
+ if (!this.joins.includes(key)) {
641
+ r[key] = [] as any
642
+ continue
643
+ }
644
+
645
+ r[key] = [Object.create(null)] as any
646
+
647
+ for (const column in template[key][0]) {
648
+ r[key][0][column] = row[`${key}_${column}`] ?? null
649
+ }
650
+ }
651
+
652
+ return r
653
+ }
654
+
655
+ public _handleStream<Y>(handler: Sql.StreamHandler): PlataPromise<Y> {
656
+ return new Promise(resolve => {
657
+ let shoudContinue = true
658
+
659
+ const stream = this.query.stream()
660
+
661
+ stream.on('close', async () => {
662
+ return resolve(handler.close<Y>())
663
+ })
664
+
665
+ stream.on('data', async row => {
666
+ if (shoudContinue) {
667
+ shoudContinue = await handler.row(this._buildModel(row))
668
+ } else {
669
+ stream.pause()
670
+ }
671
+ })
672
+
673
+ stream.on('pause', () => {
674
+ stream.destroy()
675
+ })
676
+
677
+ stream.on('error', async err => {
678
+ shoudContinue = false
679
+
680
+ if (handler.error !== undefined) await handler.error(err)
681
+
682
+ return resolve({
683
+ errorID: 'PLSQLQ001',
684
+ msg: 'Unexpected error in query',
685
+ error: {
686
+ message: err?.toString(),
687
+ stack: err?.stack ?? new Error().stack
688
+ }
689
+ })
690
+ })
691
+ })
692
+ }
693
+
694
+ public async toListAsync(): Sql.ModelList<T> {
695
+ const errors: PlataError[] = []
696
+ const itens: any[] = []
697
+
698
+ const result: PlataResult<boolean[]> = await this.query.then(
699
+ r => {
700
+ const promises: Promise<boolean>[] = []
701
+
702
+ for (let i = 0; i < r.length; i++) {
703
+ promises.push(Plata.FastPromise(async () => {
704
+ const v = await this.model.validate(this._buildModel(r[i]))
705
+
706
+ if (v.errors !== undefined) {
707
+ errors.push(...v.errors)
708
+ }
709
+
710
+ itens.push(v.value)
711
+
712
+ return true
713
+ }))
714
+ }
715
+
716
+ return Promise.all(promises)
717
+ },
718
+ err => Plata.BuildPlataError({
719
+ errorID: 'PLSQLQ005',
720
+ msg: 'Erro ao execultar a query',
721
+ error: {
722
+ message: err?.toString(),
723
+ stack: err?.stack ?? new Error().stack
724
+ }
725
+ })
726
+ )
727
+
728
+ if (result.errorID !== undefined) {
729
+ return result
730
+ }
731
+
732
+ if (errors.length !== 0) {
733
+ return {
734
+ errors,
735
+ itens,
736
+ }
737
+ }
738
+
739
+ return {
740
+ errors: undefined,
741
+ itens,
742
+ }
743
+ }
744
+
745
+ public async eachRowAsync(c: Sql.EachRowCallBack<T>): PlataPromise<boolean> {
746
+ const promises: Promise<void>[] = []
747
+ let shoudContinue = true
748
+ let error: PlataError | undefined = undefined
749
+
750
+ return this._handleStream<MaybePlataError>({
751
+ row: async row => {
752
+ promises.push(Plata.FastPromise(() => this.model.validate(row))
753
+ .then(
754
+ lineResult => {
755
+ c(lineResult).then(r => {
756
+ if (r.errorID !== undefined) {
757
+ shoudContinue = false
758
+ error = r
759
+ return
760
+ }
761
+
762
+ shoudContinue = shoudContinue ? r : shoudContinue
763
+ })
764
+ }
765
+ )
766
+ .catch(err => {
767
+ shoudContinue = false
768
+ error = {
769
+ errorID: 'PLSQLQ003',
770
+ msg: 'Unexpected error in callback',
771
+ error: {
772
+ message: err?.toString(),
773
+ stack: err?.stack ?? new Error().stack
774
+ }
775
+ }
776
+ })
777
+ )
778
+
779
+ return shoudContinue
780
+ },
781
+ close: async (): PlataPromise<boolean> => {
782
+ return Promise.all(promises).then(
783
+ () => error ?? true,
784
+ err => Plata.BuildPlataError({
785
+ errorID: 'PLSQLQ004',
786
+ msg: 'Unexpected Error in select',
787
+ error: err
788
+ })
789
+ )
790
+ }
791
+ })
792
+ }
789
793
  }