imodel-pg 0.1.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.
Files changed (3) hide show
  1. package/index.d.mts +26 -0
  2. package/index.mjs +1248 -0
  3. package/package.json +14 -0
package/index.d.mts ADDED
@@ -0,0 +1,26 @@
1
+ /*!
2
+ * imodel v0.1.0
3
+ * (c) 2019-2025 undefined
4
+ * @license undefined
5
+ */
6
+
7
+ import pg from 'pg';
8
+ import { IConnection } from 'imodel';
9
+
10
+ /**
11
+ *
12
+ * @param {pg.Pool | (() => pg.PoolClient | PromiseLike<pg.PoolClient>)} [pool]
13
+ * @param {(client?: pg.PoolClient?) => Promise<string>} [version]
14
+ * @returns {IConnection<PgEnvTrans>}
15
+ */
16
+ declare function _default(pool?: pg.Pool | (() => pg.PoolClient | PromiseLike<pg.PoolClient>), version?: (client?: pg.PoolClient | null) => Promise<string>): IConnection<PgEnvTrans>;
17
+ /**
18
+ * @param {pg.PoolClient} client
19
+ * @returns {PgEnvTrans}
20
+ */
21
+ declare function toTransaction(client: pg.PoolClient): PgEnvTrans;
22
+ type PgEnvTrans = {
23
+ client?: pg.PoolClient | null;
24
+ };
25
+
26
+ export { type PgEnvTrans, _default as default, toTransaction };
package/index.mjs ADDED
@@ -0,0 +1,1248 @@
1
+ /*!
2
+ * imodel v0.1.0
3
+ * (c) 2019-2025 undefined
4
+ * @license undefined
5
+ */
6
+
7
+ import pg from 'pg';
8
+ import Sql from 'tagged-sql';
9
+
10
+ /**
11
+ *
12
+ * @param {string} s
13
+ * @param {object} options
14
+ * @param {number} [options.precision] 精确度
15
+ * @param {number} [options.scale] 缩放比
16
+ * @param {number} [options.size] 占用空间大小
17
+ * @param {boolean} [define]
18
+ * @returns
19
+ */
20
+ function getType(s, {
21
+ precision,
22
+ scale,
23
+ size
24
+ }, define) {
25
+ switch (s) {
26
+ case 'id':
27
+ return define ? 'bigserial' : 'bigint';
28
+ case 'rid':
29
+ return 'bigint';
30
+ case 'ref':
31
+ return 'string';
32
+ case 'i2':
33
+ case 'i4':
34
+ case 'i8':
35
+ case 'u2':
36
+ case 'u4':
37
+ case 'u8':
38
+ case 'ref16':
39
+ case 'i16':
40
+ case 'seq16':
41
+ return 'smallint';
42
+ case 'u16':
43
+ case 'i32':
44
+ case 'seq32':
45
+ return 'integer';
46
+ case 'u32':
47
+ case 'ref52':
48
+ case 'ref64':
49
+ case 'i52':
50
+ case 'i64':
51
+ case 'u64':
52
+ case 'seq64':
53
+ return 'bigint';
54
+ case 'f32':
55
+ return 'real';
56
+ case 'f64':
57
+ return 'double precision';
58
+ case 'decimal':
59
+ case 'numeric':
60
+ if (!precision) {
61
+ return s;
62
+ }
63
+ if (!scale) {
64
+ return `${s}(${precision})`;
65
+ }
66
+ return `${s}(${precision}, ${scale})`;
67
+ case 'char':
68
+ return size ? `char(${size})` : `char`;
69
+ case 'string':
70
+ return size ? `varchar(${size || 255})` : `varchar`;
71
+ case 'text':
72
+ return 'text';
73
+ case 'bin':
74
+ return 'bytea';
75
+ case 'timestamp':
76
+ {
77
+ if (!precision) {
78
+ return `timestamp with time zone`;
79
+ }
80
+ return `timestamp(${precision}) with time zone`;
81
+ }
82
+ case 'date':
83
+ return 'date';
84
+ case 'datetime':
85
+ {
86
+ if (!precision) {
87
+ return `datetime without time zone`;
88
+ }
89
+ return `datetime(${precision}) without time zone`;
90
+ }
91
+ case 'time':
92
+ {
93
+ if (!precision) {
94
+ return `time without time zone`;
95
+ }
96
+ return `time(${precision}) without time zone`;
97
+ }
98
+ case 'timetz':
99
+ {
100
+ if (!precision) {
101
+ return `time with time zone`;
102
+ }
103
+ return `time(${precision}) with time zone`;
104
+ }
105
+ case 'interval':
106
+ {
107
+ if (!precision) {
108
+ return 'interval';
109
+ }
110
+ return `interval(${precision})`;
111
+ }
112
+ case 'bool':
113
+ return 'boolean';
114
+ case 'ip':
115
+ case 'ipv4':
116
+ case 'ipv6':
117
+ return 'cidr';
118
+ case 'mac48':
119
+ return 'macaddr';
120
+ case 'mac64':
121
+ return 'macaddr8';
122
+ case 'uuid':
123
+ return 'uuid';
124
+ case 'object':
125
+ return 'jsonb';
126
+ }
127
+ }
128
+
129
+ /** @import { Environment, ColumnOptions } from 'imodel' */
130
+ /**
131
+ *
132
+ * @param {any} v
133
+ * @param {string} type
134
+ * @returns
135
+ */
136
+ function toSqlString(v, type) {
137
+ if (typeof v === 'bigint' || typeof v === 'number') {
138
+ return String(v);
139
+ }
140
+ if (typeof v === 'string') {
141
+ return `'${v.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}'`;
142
+ }
143
+ if (v === true) {
144
+ return 'TRUE';
145
+ }
146
+ if (v === false) {
147
+ return 'FALSE';
148
+ }
149
+ if (v instanceof Date) {
150
+ return `'${v.toISOString()}'`;
151
+ }
152
+ }
153
+ /**
154
+ *
155
+ * @param {Environment} env
156
+ * @param {any} value
157
+ * @param {string} type
158
+ * @param {boolean} [array]
159
+ * @returns
160
+ */
161
+ function getDefault(env, value, type, array) {
162
+ if (value === undefined) {
163
+ return;
164
+ }
165
+ if (value === env.values.now) {
166
+ return Sql`now()`;
167
+ }
168
+ const v = typeof value === 'function' ? value() : value;
169
+ if (v === undefined) {
170
+ return;
171
+ }
172
+ if (v instanceof Sql) {
173
+ return v;
174
+ }
175
+ if (v === null) {
176
+ return Sql`NULL`;
177
+ }
178
+ if (array) {
179
+ const values = Array.isArray(v) ? v : [v];
180
+ const t = values.map(v => toSqlString(v)).filter(Boolean).join(',');
181
+ return Sql(`'{${t.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}}'`);
182
+ }
183
+ const t = toSqlString(v);
184
+ if (t) {
185
+ return Sql(t);
186
+ }
187
+ }
188
+ /**
189
+ *
190
+ * @param {string} name
191
+ * @param {string} type
192
+ * @param {ColumnOptions<any>} columnOptions
193
+ * @param {Environment} env
194
+ * @returns {Sql}
195
+ */
196
+ function add(name, type, {
197
+ unique,
198
+ nullable = true,
199
+ default: defaultValue,
200
+ array,
201
+ precision,
202
+ scale,
203
+ size,
204
+ serial
205
+ }, env) {
206
+ const t = getType(type, {
207
+ precision,
208
+ scale,
209
+ size
210
+ }, serial);
211
+ const defValueSql = getDefault(env, defaultValue, type, array);
212
+ return Sql(Sql`ADD COLUMN ${Sql.Field(name)}`, Sql(`${t}${array ? '[]' : ''}`), nullable ? Sql`` : Sql`NOT NULL`, defValueSql ? Sql`DEFAULT ${defValueSql}` : Sql``);
213
+ }
214
+ /**
215
+ *
216
+ * @param {string} column
217
+ * @param {string} newName
218
+ * @returns {Sql}
219
+ */
220
+ function rename(column, newName) {
221
+ return Sql`RENAME COLUMN ${Sql.Field(column)} TO ${Sql.Field(newName)}`;
222
+ }
223
+ /**
224
+ *
225
+ * @param {string} column
226
+ * @returns {Sql}
227
+ */
228
+ function drop(column) {
229
+ return Sql`DROP COLUMN ${Sql.Table(column)}`;
230
+ }
231
+ /**
232
+ *
233
+ * @param {string} name
234
+ * @param {string} type
235
+ * @param {object} options
236
+ * @param {boolean} [options.array] 是否为数组格式
237
+ * @param {number} [options.precision] 精确度
238
+ * @param {number} [options.scale] 缩放比
239
+ * @param {number} [options.size] 占用空间大小
240
+ * @returns
241
+ */
242
+ function changeType(name, type, {
243
+ precision,
244
+ scale,
245
+ size,
246
+ array
247
+ }) {
248
+ const t = getType(type, {
249
+ precision,
250
+ scale,
251
+ size
252
+ });
253
+ const field = Sql.Field(name);
254
+ return Sql`ALTER COLUMN ${field} TYPE ${Sql(`${t}${array ? '[]' : ''}`)}`;
255
+ }
256
+ /**
257
+ *
258
+ * @param {string} name
259
+ * @param {boolean} [nullable]
260
+ * @returns
261
+ */
262
+ function changeNull(name, nullable) {
263
+ return Sql(Sql`ALTER COLUMN ${Sql.Field(name)}`, nullable ? Sql`DROP NOT NULL` : Sql`SET NOT NULL`);
264
+ }
265
+ /**
266
+ *
267
+ * @param {Environment} env
268
+ * @param {string} name
269
+ * @param {any} defValue
270
+ * @param {string} type
271
+ * @param {boolean} [array]
272
+ * @returns
273
+ */
274
+ function changeDefault(env, name, defValue, type, array) {
275
+ const v = getDefault(env, defValue, type, array);
276
+ return Sql(Sql`ALTER COLUMN ${Sql.Field(name)}`, v ? Sql`DEFAULT ${v}` : Sql` DROP DEFAULT`);
277
+ }
278
+
279
+ /** @import { Environment, ColumnOptions } from 'imodel' */
280
+ /**
281
+ *
282
+ * @param {string} table
283
+ * @param {string} name
284
+ * @param {string} type
285
+ * @param {ColumnOptions<any>} options
286
+ * @param {Environment} env
287
+ * @returns {Sql}
288
+ */
289
+ function addColumn(table, name, type, options, env) {
290
+ return Sql`ALTER TABLE ${Sql.Table(table)} ${add(name, type, options, env)}`;
291
+ }
292
+
293
+ /** @import { Environment, ColumnOptions } from 'imodel' */
294
+ /**
295
+ *
296
+ * @param {string} table
297
+ * @param {string} name
298
+ * @param {string} type
299
+ * @param {ColumnOptions<any>} columnOptions
300
+ * @param {Environment} env
301
+ * @returns {Sql}
302
+ */
303
+ function changeColumn(table, name, type, {
304
+ nullable,
305
+ default: defValue,
306
+ array,
307
+ precision,
308
+ scale,
309
+ size
310
+ }, env) {
311
+ return Sql(Sql`ALTER TABLE ${Sql.Table(table)}`, changeType(name, type, {
312
+ precision,
313
+ scale,
314
+ size,
315
+ array
316
+ }), changeNull(name, nullable), changeDefault(env, name, defValue, type, array));
317
+ }
318
+
319
+ /**
320
+ *
321
+ * @param {string} table
322
+ * @param {string} name
323
+ * @param {boolean} nullable
324
+ * @returns {Sql}
325
+ */
326
+ function changeColumnNull(table, name, nullable) {
327
+ return Sql(Sql`ALTER TABLE ${Sql.Table(table)}`, changeNull(name, nullable));
328
+ }
329
+
330
+ /**
331
+ *
332
+ * @param {string} table
333
+ * @param {string} column
334
+ * @returns {Sql}
335
+ */
336
+ function dropColumn(table, column) {
337
+ return Sql`ALTER TABLE ${Sql.Table(table)} ${drop(column)}`;
338
+ }
339
+
340
+ /**
341
+ *
342
+ * @param {string} table
343
+ * @param {string} column
344
+ * @param {string} newName
345
+ * @returns {Sql}
346
+ */
347
+ function renameColumn(table, column, newName) {
348
+ const t = Sql.Table(table);
349
+ return Sql`ALTER TABLE${t}${rename(column, newName)}`;
350
+ }
351
+
352
+ /** @import { IndexOptions } from 'imodel' */
353
+ /**
354
+ *
355
+ * @param {string} table
356
+ * @param {string} name
357
+ * @param {string[]} fields
358
+ * @param {IndexOptions} [indexOptions]
359
+ * @returns {Sql}
360
+ */
361
+ function addIndex(table, name, fields, {
362
+ unique,
363
+ include
364
+ } = {}) {
365
+ const create = unique ? Sql`CREATE UNIQUE INDEX` : Sql`CREATE INDEX`;
366
+ const id = Sql.Id(`${table}#${name}`);
367
+ const allColumns = Sql`, `.glue(...fields.map(v => Sql.Field(v)));
368
+ const fieldSqls = Sql`ON ${Sql.Table(table)}(${allColumns})`;
369
+ const includes = include?.length ? Sql`INCLUDE (${Sql`, `.glue(...include.map(v => Sql.Field(v)))})` : undefined;
370
+ return Sql(create, id, fieldSqls, includes);
371
+ }
372
+
373
+ /**
374
+ *
375
+ * @param {string} table
376
+ * @param {string} name
377
+ * @returns {Sql}
378
+ */
379
+ function dropIndex(table, name) {
380
+ return Sql`DROP INDEX ${Sql.Id(`${table}#${name}`)}`;
381
+ }
382
+
383
+ /** @import { Environment, Column } from 'imodel' */
384
+ /**
385
+ *
386
+ * @param {string} table
387
+ * @param {Column<any>[]} fields
388
+ * @param {Environment} env
389
+ * @returns {Sql}
390
+ */
391
+ function createTable(table, fields, env) {
392
+ const fieldsSql = fields.map(({
393
+ name,
394
+ type,
395
+ nullable = true,
396
+ serial,
397
+ default: defaultValue,
398
+ array,
399
+ precision,
400
+ scale,
401
+ size
402
+ }) => {
403
+ const t = getType(type, {
404
+ precision,
405
+ scale,
406
+ size
407
+ }, serial);
408
+ const typeSql = Sql(`${t}${array ? '[]' : ''}`);
409
+ const nullSql = nullable ? Sql`` : Sql`NOT NULL`;
410
+ const defValueSql = getDefault(env, defaultValue, type, array);
411
+ const defaultSql = defValueSql ? Sql`DEFAULT ${defValueSql}` : Sql``;
412
+ return Sql(Sql.Field(name), typeSql, nullSql, defaultSql);
413
+ });
414
+ const primary = fields.filter(v => v.primary).map(v => v.name);
415
+ if (primary.length) {
416
+ const c = primary.map(c => Sql.Field(c));
417
+ fieldsSql.push(Sql` PRIMARY KEY (${Sql`,`.glue(...c)})`);
418
+ }
419
+ const sql = Sql`CREATE TABLE ${Sql.Table(table)}
420
+ (${Sql`,`.glue(...fieldsSql)})`;
421
+ return sql;
422
+ }
423
+
424
+ /**
425
+ *
426
+ * @param {string} table
427
+ * @returns {Sql}
428
+ */
429
+ function dropTable(table) {
430
+ return Sql`DROP TABLE ${Sql.Table(table)}`;
431
+ }
432
+
433
+ /**
434
+ *
435
+ * @param {string} table
436
+ * @param {string} newName
437
+ * @returns {Sql}
438
+ */
439
+ function renameTable(table, newName) {
440
+ const t = Sql.Table(table);
441
+ const nn = Sql.Table(newName);
442
+ return Sql`ALTER TABLE ${t} RENAME TO ${nn}`;
443
+ }
444
+
445
+ /**
446
+ *
447
+ * @param {string} view
448
+ * @param {Sql} query
449
+ * @returns {Sql}
450
+ */
451
+ function createView(view, query) {
452
+ return Sql`CREATE VIEW ${Sql.Table(view)} ${query}`;
453
+ }
454
+
455
+ /**
456
+ *
457
+ * @param {string} view
458
+ * @returns {Sql}
459
+ */
460
+ function dropView(view) {
461
+ return Sql`DROP VIEW ${Sql.Table(view)}`;
462
+ }
463
+
464
+ /**
465
+ *
466
+ * @param {string} view
467
+ * @returns {Sql}
468
+ */
469
+ function dropMaterializedView(view) {
470
+ return Sql`DROP MATERIALIZED VIEW ${Sql.Table(view)}`;
471
+ }
472
+
473
+ /**
474
+ *
475
+ * @param {string} view
476
+ * @param {Sql} query
477
+ * @returns {Sql}
478
+ */
479
+ function createMaterializedView(view, query) {
480
+ return Sql`CREATE MATERIALIZED VIEW ${Sql.Table(view)} ${query}`;
481
+ }
482
+
483
+ /**
484
+ *
485
+ * @param {[string, boolean]} it
486
+ * @returns {Sql | Sql.Field}
487
+ */
488
+ function getOrderItem(it) {
489
+ const [f, d] = it;
490
+ if (d) {
491
+ return Sql`${Sql.Field(f)} DESC`;
492
+ }
493
+ return Sql.Field(f);
494
+ }
495
+ /**
496
+ *
497
+ * @param {[string, boolean][]} [sort]
498
+ * @returns
499
+ */
500
+ function getOrder(sort) {
501
+ if (!sort) {
502
+ return undefined;
503
+ }
504
+ if (!sort.length) {
505
+ return undefined;
506
+ }
507
+ return Sql`ORDER BY ${Sql`,`.glue(...sort.map(getOrderItem))}`;
508
+ }
509
+
510
+ /** @import { Select } from 'imodel' */
511
+ /**
512
+ *
513
+ * @param {Sql.Table} table
514
+ * @param {string} k
515
+ * @param {Select | Sql | Sql.Field} v
516
+ * @returns
517
+ */
518
+ function getField(table, k, v) {
519
+ if (!v) {
520
+ return;
521
+ }
522
+ if (v instanceof Sql.Field) {
523
+ return v.table ? v : table.field(v.field);
524
+ }
525
+ if (v instanceof Sql) {
526
+ return v;
527
+ }
528
+ const f = v.table ? Sql.Field(v.field, v.table) : table.field(v.field);
529
+ const {
530
+ fn
531
+ } = v;
532
+ if (!fn) {
533
+ return f;
534
+ }
535
+ return Sql.FnExpr(fn, [f]);
536
+ }
537
+ /**
538
+ *
539
+ * @param {Sql.Table} table
540
+ * @param {([string, Select] | Sql.Like | Sql.Field)[]} select
541
+ * @returns {Sql}
542
+ */
543
+ function getSelect(table, select) {
544
+ /** @type {(Sql.Like | Sql.Field)[]} */
545
+ const selectList = [];
546
+ for (const s of select) {
547
+ if (Sql.isLike(s) || s instanceof Sql.Field) {
548
+ selectList.push(s);
549
+ continue;
550
+ }
551
+ if (!Array.isArray(s)) {
552
+ continue;
553
+ }
554
+ const [k, v] = s;
555
+ const f = getField(table, k, v);
556
+ if (!f) {
557
+ continue;
558
+ }
559
+ selectList.push(Sql`${f} AS ${Sql.Id(k)}`);
560
+ }
561
+ return Sql`, `.glue(...selectList);
562
+ }
563
+
564
+ /** @import { Environment, WhereValue } from 'imodel' */
565
+ /**
566
+ *
567
+ * @param {string | {table?: string; field: string}} k
568
+ */
569
+ function toField(k) {
570
+ if (typeof k === 'string') {
571
+ return Sql.Field(k);
572
+ }
573
+ const {
574
+ table,
575
+ field
576
+ } = k;
577
+ if (!table) {
578
+ return Sql.Field(field);
579
+ }
580
+ return Sql.Field(field, table);
581
+ }
582
+ /**
583
+ *
584
+ * @param {Environment} env
585
+ * @param {WhereValue[]?} [wheres]
586
+ * @returns {Sql | undefined}
587
+ */
588
+ function getWhere2(env, wheres) {
589
+ if (!wheres?.length) {
590
+ return;
591
+ }
592
+ /**
593
+ *
594
+ * @param {WhereValue} where
595
+ * @returns {Sql?}
596
+ */
597
+ function toSql(where) {
598
+ if (where instanceof Sql) {
599
+ return where;
600
+ }
601
+ if (where.where) {
602
+ let q = Sql([...where.where], ...where.values);
603
+ if (where.not) {
604
+ return Sql`NOT(${q})`;
605
+ }
606
+ return q;
607
+ }
608
+ if (where.or) {
609
+ /** @type {Sql[]} */
610
+ const orList = [];
611
+ for (const list of where.or) {
612
+ /** @type {Sql[]} */
613
+ // @ts-ignore
614
+ const andList = list.map(v => toSql(v)).filter(Boolean);
615
+ if (andList.length) {
616
+ orList.push(Sql` AND `.glue(...andList));
617
+ }
618
+ }
619
+ if (!orList.length) {
620
+ return null;
621
+ }
622
+ if (where.not) {
623
+ return Sql`not(${Sql` OR `.glue(...orList)})`;
624
+ }
625
+ return Sql`(${Sql` OR `.glue(...orList)})`;
626
+ }
627
+ const {
628
+ field: k,
629
+ operator: o,
630
+ value: v
631
+ } = where;
632
+ if (Array.isArray(k)) {
633
+ let {
634
+ not
635
+ } = where;
636
+ const field = Sql`(${Sql`,`.glue(k.map(toField))})`;
637
+ let values = v;
638
+ switch (o?.toUpperCase().replaceAll('_', '').replaceAll(' ', '')) {
639
+ case 'NOTIN':
640
+ case 'NOT':
641
+ case '!=':
642
+ case '<>':
643
+ not = !not;
644
+ break;
645
+ }
646
+ console.log(v);
647
+ values = Array.isArray(v) ? Sql`(${Sql`,`.glue(v.map(v => Sql`(${Array.isArray(v) && v.length ? Sql(['', ...Array(v.length - 1).fill(','), ''], ...v.map(v => v ?? null)) : v ?? null})`))})` : Sql`(${v ?? null})`;
648
+ return Sql`${field} ${Sql(not ? `NOT IN` : `IN`)} ${values}`;
649
+ }
650
+ let {
651
+ not
652
+ } = where;
653
+ let operator = (o || '').toUpperCase() || '=';
654
+ let values = v;
655
+ switch (operator.replaceAll('_', '').replaceAll(' ', '')) {
656
+ case 'NOTIN':
657
+ not = !not;
658
+ operator = 'IN';
659
+ break;
660
+ case 'NOTLIKE':
661
+ not = !not;
662
+ operator = 'LIKE';
663
+ break;
664
+ case 'NOTCONTAINS':
665
+ not = !not;
666
+ case 'CONTAINS':
667
+ {
668
+ if (typeof values === 'string') {
669
+ values = `%${values.replaceAll('%', '\\%')}%`;
670
+ operator = 'LIKE';
671
+ }
672
+ break;
673
+ }
674
+ case 'STARTSWITH':
675
+ {
676
+ if (typeof values === 'string') {
677
+ values = `${values.replaceAll('%', '\\%')}%`;
678
+ operator = 'LIKE';
679
+ }
680
+ break;
681
+ }
682
+ case 'ENDSWITH':
683
+ {
684
+ if (typeof values === 'string') {
685
+ values = `%${values.replaceAll('%', '\\%')}`;
686
+ operator = 'LIKE';
687
+ }
688
+ break;
689
+ }
690
+ }
691
+ if (not) {
692
+ switch (operator) {
693
+ case 'IN':
694
+ operator = 'NOT IN';
695
+ not = false;
696
+ break;
697
+ case 'LIKE':
698
+ operator = 'NOT LIKE';
699
+ not = false;
700
+ break;
701
+ }
702
+ }
703
+ if (Array.isArray(v) && (operator === 'IN' || operator === 'NOT IN') && v.length) {
704
+ values = Sql(['', ...Array(v.length - 1).fill(','), ''], ...v.map(v => v ?? null));
705
+ } else {
706
+ values = Sql`${values}`;
707
+ }
708
+ if (!not) {
709
+ return Sql`${toField(k)} ${Sql(operator)} ${values}`;
710
+ }
711
+ return Sql`not(${toField(k)} ${Sql(operator)} ${values})`;
712
+ }
713
+ /** @type {Sql[]} */
714
+ // @ts-ignore
715
+ const andList = wheres.map(v => toSql(v)).filter(Boolean);
716
+ if (!andList) {
717
+ return;
718
+ }
719
+ return Sql` AND `.glue(...andList);
720
+ }
721
+ /**
722
+ *
723
+ * @param {Environment} env
724
+ * @param {WhereValue[]?} [where]
725
+ * @returns {Sql | undefined}
726
+ */
727
+ function getWhere(env, where) {
728
+ const andList = getWhere2(env, where);
729
+ if (!andList) {
730
+ return;
731
+ }
732
+ return Sql`WHERE ${andList}`;
733
+ }
734
+
735
+ /** @import { Environment, SelectArg } from 'imodel' */
736
+ /**
737
+ *
738
+ * @param {Environment} env
739
+ * @param {SelectArg} param1
740
+ * @returns {Sql}
741
+ */
742
+ function selectSql(env, {
743
+ table,
744
+ fields,
745
+ alias,
746
+ select,
747
+ where,
748
+ sort,
749
+ offset,
750
+ limit
751
+ }) {
752
+ const Table = alias ? Sql.Table(table, alias) : Sql.Table(table);
753
+ const selectSql = getSelect(Table, select);
754
+ const orderSql = getOrder(sort);
755
+ const limitSql = limit && limit > 0 ? Sql('LIMIT', limit) : undefined;
756
+ const offsetSql = offset && offset > 0 ? Sql('OFFSET', offset) : undefined;
757
+ return Sql(Sql`SELECT ${selectSql} FROM ${Table}`, getWhere(env, where), orderSql, limitSql, offsetSql);
758
+ }
759
+
760
+ /** @import { Environment, CountArg } from 'imodel' */
761
+ /**
762
+ *
763
+ * @param {Environment} env
764
+ * @param {CountArg} arg
765
+ * @returns {Sql}
766
+ */
767
+ function countSql(env, {
768
+ table,
769
+ alias,
770
+ where
771
+ }) {
772
+ const Table = alias ? Sql.Table(table, alias) : Sql.Table(table);
773
+ return Sql`SELECT count(*) AS count FROM ${Table} ${getWhere(env, where)}`;
774
+ }
775
+
776
+ /** @import { Environment, Fields, FieldType, WhereValue } from 'imodel' */
777
+ /**
778
+ *
779
+ * @param {Environment} env
780
+ * @param {string} table
781
+ * @param {Fields<keyof FieldType>} [fields]
782
+ * @param {WhereValue[]?} [where]
783
+ * @param {string[]?} [returning]
784
+ * @returns {Sql}
785
+ */
786
+ function deleteSql(env, table, fields, where, returning) {
787
+ const returnSql = returning ? Sql`RETURNING ${Sql`, `.glue(...returning.map(k => Sql.Field(k)))}` : undefined;
788
+ return Sql(Sql`DELETE FROM ${Sql.Table(table)}`, getWhere(env, where), returnSql);
789
+ }
790
+
791
+ /** @import { Environment, FieldDefine, Fields, FieldType } from 'imodel' */
792
+ /**
793
+ *
794
+ * @param {object} data
795
+ * @param {string} key
796
+ * @param {FieldDefine<keyof FieldType, boolean, boolean>} field
797
+ * @param {Environment} env
798
+ * @returns
799
+ */
800
+ function getValue(data, key, field, env) {
801
+ const val = data[key];
802
+ if (val === env.values.now) {
803
+ return Sql`now()`;
804
+ }
805
+ if (val !== undefined) {
806
+ return val;
807
+ }
808
+ const def = field.default;
809
+ return typeof def === 'function' ? def(data) ?? null : def ?? null;
810
+ }
811
+ /**
812
+ *
813
+ * @param {string} table
814
+ * @param {Fields<keyof FieldType>} fields
815
+ * @param {object[]} list
816
+ * @param {string[]} keys
817
+ * @param {Environment} env
818
+ * @returns {Sql}
819
+ */
820
+ function insertSql(table, fields, list, keys, env) {
821
+ const key = new Set(keys);
822
+ const keyData = Object.entries(fields).filter(([f]) => key.has(f));
823
+ const values = list.map(v => Sql`(${Sql`,`.glue(...keyData.map(([k, f]) => Sql`${getValue(v, k, f, env)}`))})`);
824
+ return Sql`INSERT INTO ${Sql.Table(table)} (${Sql`,`.glue(...keyData.map(([k]) => Sql.Field(k)))}) VALUES ${Sql`,`.glue(...values)} RETURNING *`;
825
+ }
826
+
827
+ /** @import { Environment, Fields, FieldType, SetValue } from 'imodel' */
828
+ /**
829
+ *
830
+ * @param {Record<string, SetValue>} update
831
+ * @param {Fields<keyof FieldType>} fields
832
+ * @param {Environment} env
833
+ * @returns {Sql[]}
834
+ */
835
+ function getSet(update, fields, env) {
836
+ const {
837
+ now,
838
+ increment,
839
+ decrement,
840
+ multiply,
841
+ divide
842
+ } = env.values;
843
+ return /** @type {Sql[]} */Object.entries(update).filter(([, v]) => v !== undefined).map(([key, value]) => {
844
+ const field = key in fields && fields[key];
845
+ if (!field) {
846
+ return;
847
+ }
848
+ const f = Sql.Field(key);
849
+ if (value instanceof Sql) {
850
+ return Sql`${f} = ${value}`;
851
+ }
852
+ if (value === now) {
853
+ return Sql`${f} = now()`;
854
+ }
855
+ if (value === null) {
856
+ return Sql`${f} = NULL`;
857
+ }
858
+ if (typeof value !== 'object') {
859
+ return Sql`${f} = ${value}`;
860
+ }
861
+ const incrementValue = value[increment];
862
+ if (incrementValue !== undefined) {
863
+ return Sql`${f} = ${f} + ${incrementValue}`;
864
+ }
865
+ const decrementValue = value[decrement];
866
+ if (decrementValue !== undefined) {
867
+ return Sql`${f} = ${f} + ${decrementValue}`;
868
+ }
869
+ const multiplyValue = value[multiply];
870
+ if (multiplyValue !== undefined) {
871
+ return Sql`${f} = ${f} + ${multiplyValue}`;
872
+ }
873
+ const divideValue = value[divide];
874
+ if (divideValue !== undefined) {
875
+ return Sql`${f} = ${f} + ${divideValue}`;
876
+ }
877
+ return Sql`${f} = ${value}`;
878
+ }).filter(Boolean);
879
+ }
880
+
881
+ /** @import { Environment, MatchArg, SetValue } from 'imodel' */
882
+ /**
883
+ *
884
+ * @param {Environment} env
885
+ * @param {MatchArg} param1
886
+ * @param {Record<string, SetValue>} update
887
+ * @param {string[]?} [returning]
888
+ * @returns {Sql}
889
+ */
890
+ function updateSql(env, {
891
+ table,
892
+ fields,
893
+ where
894
+ }, update, returning) {
895
+ const setSql = Sql`, `.glue(...getSet(update, fields, env));
896
+ const returnSql = returning ? Sql`RETURNING ${Sql`, `.glue(...returning.map(k => Sql.Field(k)))}` : undefined;
897
+ return Sql(Sql`UPDATE ${Sql.Table(table)} SET ${setSql}`, getWhere(env, where), returnSql);
898
+ }
899
+
900
+ /** @import { Environment, Fields, FieldType, WhereValue } from 'imodel' */
901
+ const tmp = Sql.Table('__tmp').as('__tmp');
902
+ const tmpId = Sql.Id('__tmp', 'alias');
903
+ /**
904
+ *
905
+ * @param {Environment} env
906
+ * @param {string} table
907
+ * @param {Fields<keyof FieldType>} fields
908
+ * @param {string[]} pKeys
909
+ * @param {string[]} setKeys
910
+ * @param {Record<string, any>[]} list
911
+ * @param {Record<string, any>} values
912
+ * @param {WhereValue[]} where
913
+ * @param {string[]} [returning]
914
+ * @returns {Sql}
915
+ */
916
+ function updateListSql(env, table, fields, pKeys, setKeys, list, values, where, returning) {
917
+ const dataFields = [...setKeys, ...pKeys];
918
+ const returnSql = returning ? Sql`RETURNING ${Sql`, `.glue(...returning.map(k => Sql.Field(k)))}` : undefined;
919
+ /** @type {[string, [string, string]][]} */
920
+ const dataFieldType = dataFields.map(f => {
921
+ const field = fields[f];
922
+ if (!field) {
923
+ return [f, ['', '']];
924
+ }
925
+ const type = `${getType(field.type, field)}${field.array ? '[]' : ''}`;
926
+ return [f, ['CAST(', ` as ${type})`]];
927
+ });
928
+ const main = Sql.Table(table);
929
+ return Sql`UPDATE ${main}
930
+ SET ${Sql`, `.glue(setKeys.map(f => Sql`${Sql.Field(f)} = ${tmp.field(f)}`), Object.entries(values).map(([f, v]) => Sql`${Sql.Field(f)} = ${v}`))}
931
+ FROM (values${Sql`,`.glue(list.map(data => Sql`(${Sql`,`.glue(dataFieldType.map(([f, t]) => {
932
+ const v = data[f] ?? null;
933
+ if (v === null) {
934
+ return Sql`${v}`;
935
+ }
936
+ return Sql(t, data[f] ?? null);
937
+ }))})`))}) as ${tmpId}(${Sql`,`.glue(dataFields.map(f => Sql.Field(f)))})
938
+ WHERE ${Sql`AND`.glue(pKeys.map(f => Sql`${main.field(f)}=${tmp.field(f)}`), getWhere2(env, where))}${returnSql}`;
939
+ }
940
+
941
+ /** @import { Environment, IConnection } from 'imodel' */
942
+ const {
943
+ types
944
+ } = pg;
945
+ types.setTypeParser(types.builtins.INT8, v => BigInt(v));
946
+ const dbType = 'postgres';
947
+ /**
948
+ *
949
+ * @param {string} text
950
+ * @param {readonly any[]} values
951
+ * @returns
952
+ */
953
+ function echo(text, values) {
954
+ if (!values.length) {
955
+ console.info(text);
956
+ return;
957
+ }
958
+ console.groupCollapsed(text);
959
+ for (let i = 0; i < values.length; i++) {
960
+ console.info(`$${i + 1}:`, values[i]);
961
+ }
962
+ console.groupEnd();
963
+ }
964
+ /**
965
+ *
966
+ * @param {() => pg.PoolClient | PromiseLike<pg.PoolClient>} connect
967
+ * @returns
968
+ */
969
+ function createVersion(connect) {
970
+ /**
971
+ *
972
+ * @param {pg.PoolClient?} [userClient]
973
+ * @returns
974
+ */
975
+ async function getVersion(userClient) {
976
+ const client = userClient || (await connect());
977
+ /** @type {pg.QueryResult<{server_version: string}>} */
978
+ const v = await client.query('show server_version');
979
+ if (!userClient) {
980
+ client.release();
981
+ }
982
+ /** 数据库版本号 */
983
+ return v.rows[0]?.server_version || '';
984
+ }
985
+ /** @type {Promise<string>?} */
986
+ let dbVersion = null;
987
+ /**
988
+ *
989
+ * @param {pg.PoolClient?} [client]
990
+ * @returns
991
+ */
992
+ return function version(client) {
993
+ dbVersion ||= getVersion(client);
994
+ return dbVersion;
995
+ };
996
+ }
997
+ /** @typedef {{client?: pg.PoolClient?}} PgEnvTrans */
998
+ /**
999
+ *
1000
+ * @param {pg.Pool | (() => pg.PoolClient | PromiseLike<pg.PoolClient>) | null} [pool]
1001
+ * @returns {() => pg.PoolClient | PromiseLike<pg.PoolClient>}
1002
+ */
1003
+ function toPoolConnection(pool) {
1004
+ if (!pool) {
1005
+ return () => {
1006
+ throw new Error();
1007
+ };
1008
+ }
1009
+ if (typeof pool === 'function') {
1010
+ return pool;
1011
+ }
1012
+ return () => pool.connect();
1013
+ }
1014
+ /**
1015
+ *
1016
+ * @param {pg.Pool | (() => pg.PoolClient | PromiseLike<pg.PoolClient>)} [pool]
1017
+ * @param {(client?: pg.PoolClient?) => Promise<string>} [version]
1018
+ * @returns {IConnection<PgEnvTrans>}
1019
+ */
1020
+ function index (pool, version) {
1021
+ const connect = toPoolConnection(pool);
1022
+ const dbVersion = typeof version === 'function' ? version : createVersion(connect);
1023
+ /**
1024
+ * @template {pg.QueryResultRow} T
1025
+ * @param {Sql} sql
1026
+ * @param {Environment<PgEnvTrans>} env
1027
+ * @returns {Promise<pg.QueryResult<T>>}
1028
+ */
1029
+ async function _exec(sql, env) {
1030
+ const text = sql.build((_, i) => `$${i + 1}`);
1031
+ const {
1032
+ values
1033
+ } = sql;
1034
+ if (env.isDevelopment) {
1035
+ echo(text, values);
1036
+ }
1037
+ const {
1038
+ transaction
1039
+ } = env;
1040
+ const transactionClient = transaction?.client;
1041
+ if (transactionClient) {
1042
+ const client = await Promise.resolve(transactionClient);
1043
+ return client.query({
1044
+ text,
1045
+ values: [...values]
1046
+ });
1047
+ }
1048
+ const client = await connect();
1049
+ try {
1050
+ return await client.query({
1051
+ text,
1052
+ values: [...values]
1053
+ });
1054
+ } finally {
1055
+ client.release();
1056
+ }
1057
+ }
1058
+ /**
1059
+ * @template {pg.QueryResultRow} T
1060
+ * @param {Environment<PgEnvTrans>} env
1061
+ * @param {Sql} sql
1062
+ * @returns {Promise<T[]>}
1063
+ */
1064
+ async function query(env, sql) {
1065
+ /** @type {pg.QueryResult<T>} */
1066
+ const result = await _exec(sql, env);
1067
+ return result.rows;
1068
+ }
1069
+ /**
1070
+ * @param {Environment<PgEnvTrans>} env
1071
+ * @param {Sql} sql
1072
+ * @returns {Promise<number>}
1073
+ */
1074
+ async function exec(env, sql) {
1075
+ const result = await _exec(sql, env);
1076
+ return result.rowCount || 0;
1077
+ }
1078
+ return {
1079
+ dbType,
1080
+ async dbVersion(env) {
1081
+ return dbVersion(env.transaction?.client);
1082
+ },
1083
+ query(env, sql, ...values) {
1084
+ const ss = Array.isArray(sql) ? Sql(sql, ...values) : sql;
1085
+ const {
1086
+ getName
1087
+ } = env;
1088
+ const s = env.names ? ss.transform((id, group) => group === 'table' ? getName(id) : id) : ss;
1089
+ return query(env, s);
1090
+ },
1091
+ exec(env, sql, ...values) {
1092
+ const ss = Array.isArray(sql) ? Sql(sql, ...values) : sql;
1093
+ const {
1094
+ getName
1095
+ } = env;
1096
+ const s = env.names ? ss.transform((id, group) => group === 'table' ? getName(id) : id) : ss;
1097
+ return exec(env, s);
1098
+ },
1099
+ async type(env, table) {
1100
+ const sql = Sql`SELECT table_type as type
1101
+ FROM information_schema.tables
1102
+ WHERE table_schema = 'public' AND table_name = ${table} LIMIT 1`;
1103
+ /** @type {pg.QueryResult<{ type: string; }>} */
1104
+ const {
1105
+ rows: [row]
1106
+ } = await _exec(sql, env);
1107
+ if (!row) {
1108
+ return null;
1109
+ }
1110
+ switch (String(row.type).toLowerCase()) {
1111
+ case 'base table':
1112
+ return 'table';
1113
+ case 'view':
1114
+ return 'view';
1115
+ default:
1116
+ return '';
1117
+ }
1118
+ },
1119
+ insert(env, table, fields, data, keys) {
1120
+ return query(env, insertSql(table, fields, data, keys, env));
1121
+ },
1122
+ select(env, arg) {
1123
+ return query(env, selectSql(env, arg));
1124
+ },
1125
+ count(env, argv) {
1126
+ return query(env, countSql(env, argv)).then(r => r[0]?.count || 0);
1127
+ },
1128
+ update(env, argv, update) {
1129
+ if (!Object.keys(update).length) {
1130
+ return Promise.resolve(0);
1131
+ }
1132
+ return exec(env, updateSql(env, argv, update));
1133
+ },
1134
+ updateReturn(env, argv, update, returning) {
1135
+ if (!Object.keys(update).length) {
1136
+ const {
1137
+ table,
1138
+ fields,
1139
+ where
1140
+ } = argv;
1141
+ return query(env, selectSql(env, {
1142
+ table,
1143
+ fields,
1144
+ where: where || [],
1145
+ select: returning.map(v => [v, {
1146
+ field: v
1147
+ }])
1148
+ }));
1149
+ }
1150
+ return query(env, updateSql(env, argv, update, returning));
1151
+ },
1152
+ updateMany(env, table, fields, update, pKeys, setKeys, list, where) {
1153
+ return exec(env, updateListSql(env, table, fields, pKeys, setKeys, list, update, where));
1154
+ },
1155
+ updateManyReturn(env, table, fields, update, pKeys, setKeys, list, where, returning) {
1156
+ return query(env, updateListSql(env, table, fields, pKeys, setKeys, list, update, where, returning));
1157
+ },
1158
+ delete(env, {
1159
+ table,
1160
+ fields,
1161
+ where
1162
+ }) {
1163
+ return exec(env, deleteSql(env, table, fields, where));
1164
+ },
1165
+ deleteReturn(env, {
1166
+ table,
1167
+ fields,
1168
+ where
1169
+ }, returning) {
1170
+ return query(env, deleteSql(env, table, fields, where, returning));
1171
+ },
1172
+ addColumn(env, table, column, type, options) {
1173
+ return exec(env, addColumn(table, column, type, options, env));
1174
+ },
1175
+ changeColumn(env, table, column, type, options) {
1176
+ return exec(env, changeColumn(table, column, type, options, env));
1177
+ },
1178
+ changeColumnNull(env, table, column, nullable) {
1179
+ return exec(env, changeColumnNull(table, column, nullable));
1180
+ },
1181
+ dropColumn(env, table, column) {
1182
+ return exec(env, dropColumn(table, column));
1183
+ },
1184
+ renameColumn(env, table, column, newName) {
1185
+ return exec(env, renameColumn(table, column, newName));
1186
+ },
1187
+ addIndex(env, table, name, fields, options) {
1188
+ return exec(env, addIndex(table, name, fields, options));
1189
+ },
1190
+ dropIndex(env, table, name) {
1191
+ return exec(env, dropIndex(table, name));
1192
+ },
1193
+ createTable(env, table, fields) {
1194
+ return exec(env, createTable(table, fields, env));
1195
+ },
1196
+ dropTable(env, table) {
1197
+ return exec(env, dropTable(table));
1198
+ },
1199
+ renameTable(env, table, newName) {
1200
+ return exec(env, renameTable(table, newName));
1201
+ },
1202
+ createView(env, view, q) {
1203
+ return exec(env, createView(view, selectSql(env, q)));
1204
+ },
1205
+ dropView(env, view) {
1206
+ return exec(env, dropView(view));
1207
+ },
1208
+ createMaterializedView(env, view, q) {
1209
+ return exec(env, createMaterializedView(view, selectSql(env, q)));
1210
+ },
1211
+ dropMaterializedView(env, view) {
1212
+ return exec(env, dropMaterializedView(view));
1213
+ },
1214
+ async transaction(env, fn) {
1215
+ if (env?.transaction?.client) {
1216
+ throw new Error();
1217
+ }
1218
+ const client = await connect();
1219
+ await client.query('BEGIN');
1220
+ /** @type {PgEnvTrans} */
1221
+ const transaction = {
1222
+ client
1223
+ };
1224
+ let error = false;
1225
+ try {
1226
+ return await fn(transaction);
1227
+ } catch (e) {
1228
+ error = true;
1229
+ throw e;
1230
+ } finally {
1231
+ transaction.client = null;
1232
+ await client.query(error ? 'ROLLBACK' : 'COMMIT');
1233
+ client.release();
1234
+ }
1235
+ }
1236
+ };
1237
+ }
1238
+ /**
1239
+ * @param {pg.PoolClient} client
1240
+ * @returns {PgEnvTrans}
1241
+ */
1242
+ function toTransaction(client) {
1243
+ return {
1244
+ client
1245
+ };
1246
+ }
1247
+
1248
+ export { index as default, toTransaction };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "imodel-pg",
3
+ "version": "0.1.0",
4
+ "dependencies": {
5
+ "pg": "^8.13.3",
6
+ "tagged-sql": "^0.9.0"
7
+ },
8
+ "main": "index.mjs",
9
+ "type": "module",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://git@gitee.com/fierflame/imodel.git"
13
+ }
14
+ }