@smartive/graphql-magic 23.4.0-next.9 → 23.4.1
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/CHANGELOG.md +2 -3
- package/dist/bin/gqm.cjs +125 -656
- package/dist/cjs/index.cjs +2118 -2685
- package/dist/esm/migrations/generate.d.ts +1 -9
- package/dist/esm/migrations/generate.js +33 -269
- package/dist/esm/migrations/generate.js.map +1 -1
- package/dist/esm/migrations/index.d.ts +0 -2
- package/dist/esm/migrations/index.js +0 -2
- package/dist/esm/migrations/index.js.map +1 -1
- package/dist/esm/models/model-definitions.d.ts +1 -4
- package/dist/esm/resolvers/filters.js +14 -76
- package/dist/esm/resolvers/filters.js.map +1 -1
- package/dist/esm/resolvers/selects.js +2 -20
- package/dist/esm/resolvers/selects.js.map +1 -1
- package/dist/esm/resolvers/utils.d.ts +0 -1
- package/dist/esm/resolvers/utils.js +0 -29
- package/dist/esm/resolvers/utils.js.map +1 -1
- package/docs/docs/3-fields.md +0 -149
- package/docs/docs/5-migrations.md +1 -9
- package/package.json +2 -2
- package/src/bin/gqm/gqm.ts +5 -44
- package/src/bin/gqm/settings.ts +0 -7
- package/src/bin/gqm/static-eval.ts +102 -0
- package/src/bin/gqm/utils.ts +0 -1
- package/src/migrations/generate.ts +41 -334
- package/src/migrations/index.ts +0 -2
- package/src/models/model-definitions.ts +1 -4
- package/src/resolvers/filters.ts +25 -88
- package/src/resolvers/selects.ts +5 -22
- package/src/resolvers/utils.ts +0 -44
- package/dist/esm/migrations/generate-functions.d.ts +0 -2
- package/dist/esm/migrations/generate-functions.js +0 -60
- package/dist/esm/migrations/generate-functions.js.map +0 -1
- package/dist/esm/migrations/types.d.ts +0 -7
- package/dist/esm/migrations/types.js +0 -2
- package/dist/esm/migrations/types.js.map +0 -1
- package/dist/esm/migrations/update-functions.d.ts +0 -3
- package/dist/esm/migrations/update-functions.js +0 -177
- package/dist/esm/migrations/update-functions.js.map +0 -1
- package/src/bin/gqm/parse-functions.ts +0 -141
- package/src/migrations/generate-functions.ts +0 -74
- package/src/migrations/types.ts +0 -7
- package/src/migrations/update-functions.ts +0 -221
|
@@ -17,20 +17,10 @@ import {
|
|
|
17
17
|
summonByName,
|
|
18
18
|
typeToField,
|
|
19
19
|
} from '../models/utils';
|
|
20
|
-
import { getColumnName } from '../resolvers';
|
|
21
20
|
import { Value } from '../values';
|
|
22
|
-
import { ParsedFunction } from './types';
|
|
23
21
|
|
|
24
22
|
type Callbacks = (() => void)[];
|
|
25
23
|
|
|
26
|
-
type DatabaseFunction = {
|
|
27
|
-
name: string;
|
|
28
|
-
signature: string;
|
|
29
|
-
body: string;
|
|
30
|
-
isAggregate: boolean;
|
|
31
|
-
definition?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
24
|
export class MigrationGenerator {
|
|
35
25
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
36
26
|
private writer: CodeBlockWriter = new CodeBlockWriter['default']({
|
|
@@ -42,14 +32,11 @@ export class MigrationGenerator {
|
|
|
42
32
|
private uuidUsed?: boolean;
|
|
43
33
|
private nowUsed?: boolean;
|
|
44
34
|
public needsMigration = false;
|
|
45
|
-
private knex: Knex;
|
|
46
35
|
|
|
47
36
|
constructor(
|
|
48
37
|
knex: Knex,
|
|
49
38
|
private models: Models,
|
|
50
|
-
private parsedFunctions?: ParsedFunction[],
|
|
51
39
|
) {
|
|
52
|
-
this.knex = knex;
|
|
53
40
|
this.schema = SchemaInspector(knex);
|
|
54
41
|
}
|
|
55
42
|
|
|
@@ -70,8 +57,6 @@ export class MigrationGenerator {
|
|
|
70
57
|
down,
|
|
71
58
|
);
|
|
72
59
|
|
|
73
|
-
await this.handleFunctions(up, down);
|
|
74
|
-
|
|
75
60
|
for (const model of models.entities) {
|
|
76
61
|
if (model.deleted) {
|
|
77
62
|
up.push(() => {
|
|
@@ -142,9 +127,7 @@ export class MigrationGenerator {
|
|
|
142
127
|
foreignKey: 'id',
|
|
143
128
|
});
|
|
144
129
|
}
|
|
145
|
-
for (const field of model.fields
|
|
146
|
-
.filter(not(isInherited))
|
|
147
|
-
.filter((f) => !(f.generateAs?.type === 'expression'))) {
|
|
130
|
+
for (const field of model.fields.filter(not(isInherited))) {
|
|
148
131
|
this.column(field);
|
|
149
132
|
}
|
|
150
133
|
});
|
|
@@ -155,8 +138,12 @@ export class MigrationGenerator {
|
|
|
155
138
|
});
|
|
156
139
|
} else {
|
|
157
140
|
// Rename fields
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
this.renameFields(
|
|
142
|
+
model,
|
|
143
|
+
model.fields.filter(not(isInherited)).filter(({ oldName }) => oldName),
|
|
144
|
+
up,
|
|
145
|
+
down,
|
|
146
|
+
);
|
|
160
147
|
|
|
161
148
|
// Add missing fields
|
|
162
149
|
this.createFields(
|
|
@@ -166,7 +153,6 @@ export class MigrationGenerator {
|
|
|
166
153
|
.filter(
|
|
167
154
|
({ name, ...field }) =>
|
|
168
155
|
field.kind !== 'custom' &&
|
|
169
|
-
!(field.generateAs?.type === 'expression') &&
|
|
170
156
|
!this.getColumn(model.name, field.kind === 'relation' ? field.foreignKey || `${name}Id` : name),
|
|
171
157
|
),
|
|
172
158
|
up,
|
|
@@ -175,7 +161,7 @@ export class MigrationGenerator {
|
|
|
175
161
|
|
|
176
162
|
// Update fields
|
|
177
163
|
const rawExistingFields = model.fields.filter((field) => {
|
|
178
|
-
if (!field.generateAs
|
|
164
|
+
if (!field.generateAs) {
|
|
179
165
|
return false;
|
|
180
166
|
}
|
|
181
167
|
|
|
@@ -184,7 +170,7 @@ export class MigrationGenerator {
|
|
|
184
170
|
return false;
|
|
185
171
|
}
|
|
186
172
|
|
|
187
|
-
if (col.generation_expression !== field.generateAs
|
|
173
|
+
if (col.generation_expression !== field.generateAs) {
|
|
188
174
|
return true;
|
|
189
175
|
}
|
|
190
176
|
|
|
@@ -194,9 +180,7 @@ export class MigrationGenerator {
|
|
|
194
180
|
this.updateFieldsRaw(model, rawExistingFields, up, down);
|
|
195
181
|
}
|
|
196
182
|
|
|
197
|
-
const existingFields = model.fields.filter(
|
|
198
|
-
(field) => (!field.generateAs || field.generateAs.type === 'expression') && this.hasChanged(model, field),
|
|
199
|
-
);
|
|
183
|
+
const existingFields = model.fields.filter((field) => !field.generateAs && this.hasChanged(model, field));
|
|
200
184
|
this.updateFields(model, existingFields, up, down);
|
|
201
185
|
}
|
|
202
186
|
|
|
@@ -228,9 +212,7 @@ export class MigrationGenerator {
|
|
|
228
212
|
writer.writeLine(`deleteRootId: row.deleteRootId,`);
|
|
229
213
|
}
|
|
230
214
|
|
|
231
|
-
for (const { name, kind } of model.fields
|
|
232
|
-
.filter(isUpdatableField)
|
|
233
|
-
.filter((f) => !(f.generateAs?.type === 'expression'))) {
|
|
215
|
+
for (const { name, kind } of model.fields.filter(isUpdatableField)) {
|
|
234
216
|
const col = kind === 'relation' ? `${name}Id` : name;
|
|
235
217
|
|
|
236
218
|
writer.writeLine(`${col}: row.${col},`);
|
|
@@ -249,23 +231,11 @@ export class MigrationGenerator {
|
|
|
249
231
|
});
|
|
250
232
|
} else {
|
|
251
233
|
const revisionTable = `${model.name}Revision`;
|
|
252
|
-
|
|
253
|
-
this.renameFields(
|
|
254
|
-
revisionTable,
|
|
255
|
-
model.fields
|
|
256
|
-
.filter(isUpdatableField)
|
|
257
|
-
.filter(not(isInherited))
|
|
258
|
-
.filter(({ oldName }) => oldName),
|
|
259
|
-
up,
|
|
260
|
-
down,
|
|
261
|
-
);
|
|
262
|
-
|
|
263
234
|
const missingRevisionFields = model.fields
|
|
264
235
|
.filter(isUpdatableField)
|
|
265
236
|
.filter(
|
|
266
237
|
({ name, ...field }) =>
|
|
267
238
|
field.kind !== 'custom' &&
|
|
268
|
-
!(field.generateAs?.type === 'expression') &&
|
|
269
239
|
!this.getColumn(revisionTable, field.kind === 'relation' ? field.foreignKey || `${name}Id` : name),
|
|
270
240
|
);
|
|
271
241
|
|
|
@@ -352,14 +322,14 @@ export class MigrationGenerator {
|
|
|
352
322
|
return writer.toString();
|
|
353
323
|
}
|
|
354
324
|
|
|
355
|
-
private renameFields(
|
|
325
|
+
private renameFields(model: EntityModel, fields: EntityField[], up: Callbacks, down: Callbacks) {
|
|
356
326
|
if (!fields.length) {
|
|
357
327
|
return;
|
|
358
328
|
}
|
|
359
329
|
|
|
360
330
|
up.push(() => {
|
|
361
331
|
for (const field of fields) {
|
|
362
|
-
this.alterTable(
|
|
332
|
+
this.alterTable(model.name, () => {
|
|
363
333
|
this.renameColumn(
|
|
364
334
|
field.kind === 'relation' ? `${field.oldName}Id` : get(field, 'oldName'),
|
|
365
335
|
field.kind === 'relation' ? `${field.name}Id` : field.name,
|
|
@@ -370,7 +340,7 @@ export class MigrationGenerator {
|
|
|
370
340
|
|
|
371
341
|
down.push(() => {
|
|
372
342
|
for (const field of fields) {
|
|
373
|
-
this.alterTable(
|
|
343
|
+
this.alterTable(model.name, () => {
|
|
374
344
|
this.renameColumn(
|
|
375
345
|
field.kind === 'relation' ? `${field.name}Id` : field.name,
|
|
376
346
|
field.kind === 'relation' ? `${field.oldName}Id` : get(field, 'oldName'),
|
|
@@ -380,7 +350,7 @@ export class MigrationGenerator {
|
|
|
380
350
|
});
|
|
381
351
|
|
|
382
352
|
for (const field of fields) {
|
|
383
|
-
summonByName(this.columns[
|
|
353
|
+
summonByName(this.columns[model.name], field.kind === 'relation' ? `${field.oldName!}Id` : field.oldName!).name =
|
|
384
354
|
field.kind === 'relation' ? `${field.name}Id` : field.name;
|
|
385
355
|
}
|
|
386
356
|
}
|
|
@@ -395,10 +365,6 @@ export class MigrationGenerator {
|
|
|
395
365
|
const updates: Callbacks = [];
|
|
396
366
|
const postAlter: Callbacks = [];
|
|
397
367
|
for (const field of fields) {
|
|
398
|
-
if (field.generateAs?.type === 'expression') {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
368
|
alter.push(() => this.column(field, { setNonNull: field.defaultValue !== undefined }));
|
|
403
369
|
|
|
404
370
|
if (field.generateAs) {
|
|
@@ -464,7 +430,7 @@ export class MigrationGenerator {
|
|
|
464
430
|
});
|
|
465
431
|
|
|
466
432
|
if (isUpdatableModel(model)) {
|
|
467
|
-
const updatableFields = fields.filter(isUpdatableField)
|
|
433
|
+
const updatableFields = fields.filter(isUpdatableField);
|
|
468
434
|
if (!updatableFields.length) {
|
|
469
435
|
return;
|
|
470
436
|
}
|
|
@@ -518,7 +484,7 @@ export class MigrationGenerator {
|
|
|
518
484
|
});
|
|
519
485
|
|
|
520
486
|
if (isUpdatableModel(model)) {
|
|
521
|
-
const updatableFields = fields.filter(isUpdatableField)
|
|
487
|
+
const updatableFields = fields.filter(isUpdatableField);
|
|
522
488
|
if (!updatableFields.length) {
|
|
523
489
|
return;
|
|
524
490
|
}
|
|
@@ -562,9 +528,7 @@ export class MigrationGenerator {
|
|
|
562
528
|
}
|
|
563
529
|
}
|
|
564
530
|
|
|
565
|
-
for (const field of model.fields
|
|
566
|
-
.filter(and(isUpdatableField, not(isInherited)))
|
|
567
|
-
.filter((f) => !(f.generateAs?.type === 'expression'))) {
|
|
531
|
+
for (const field of model.fields.filter(and(isUpdatableField, not(isInherited)))) {
|
|
568
532
|
this.column(field, { setUnique: false, setDefault: false });
|
|
569
533
|
}
|
|
570
534
|
});
|
|
@@ -582,31 +546,23 @@ export class MigrationGenerator {
|
|
|
582
546
|
});
|
|
583
547
|
|
|
584
548
|
// Insert data for missing revisions columns
|
|
585
|
-
|
|
586
|
-
(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
)
|
|
603
|
-
.newLine();
|
|
604
|
-
}
|
|
605
|
-
})
|
|
606
|
-
.write(');')
|
|
607
|
-
.newLine()
|
|
608
|
-
.blankLine();
|
|
609
|
-
}
|
|
549
|
+
this.writer
|
|
550
|
+
.write(`await knex('${model.name}Revision').update(`)
|
|
551
|
+
.inlineBlock(() => {
|
|
552
|
+
for (const { name, kind: type } of missingRevisionFields) {
|
|
553
|
+
const col = type === 'relation' ? `${name}Id` : name;
|
|
554
|
+
this.writer
|
|
555
|
+
.write(
|
|
556
|
+
`${col}: knex.raw('(select "${col}" from "${model.name}" where "${model.name}".id = "${
|
|
557
|
+
model.name
|
|
558
|
+
}Revision"."${typeToField(model.name)}Id")'),`,
|
|
559
|
+
)
|
|
560
|
+
.newLine();
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
.write(');')
|
|
564
|
+
.newLine()
|
|
565
|
+
.blankLine();
|
|
610
566
|
|
|
611
567
|
const nonNullableMissingRevisionFields = missingRevisionFields.filter(({ nonNull }) => nonNull);
|
|
612
568
|
if (nonNullableMissingRevisionFields.length) {
|
|
@@ -690,7 +646,7 @@ export class MigrationGenerator {
|
|
|
690
646
|
}
|
|
691
647
|
|
|
692
648
|
private renameColumn(from: string, to: string) {
|
|
693
|
-
this.writer.writeLine(`table.renameColumn('${from}', '${to}')
|
|
649
|
+
this.writer.writeLine(`table.renameColumn('${from}', '${to}')`);
|
|
694
650
|
}
|
|
695
651
|
|
|
696
652
|
private value(value: Value) {
|
|
@@ -725,10 +681,6 @@ export class MigrationGenerator {
|
|
|
725
681
|
};
|
|
726
682
|
const kind = field.kind;
|
|
727
683
|
if (field.generateAs) {
|
|
728
|
-
if (field.generateAs.type === 'expression') {
|
|
729
|
-
throw new Error(`Expression fields cannot be created in SQL schema.`);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
684
|
let type = '';
|
|
733
685
|
switch (kind) {
|
|
734
686
|
case undefined:
|
|
@@ -737,9 +689,6 @@ export class MigrationGenerator {
|
|
|
737
689
|
case 'Float':
|
|
738
690
|
type = `decimal(${field.precision ?? 'undefined'}, ${field.scale ?? 'undefined'})`;
|
|
739
691
|
break;
|
|
740
|
-
case 'Boolean':
|
|
741
|
-
type = 'boolean';
|
|
742
|
-
break;
|
|
743
692
|
default:
|
|
744
693
|
throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
|
|
745
694
|
}
|
|
@@ -759,10 +708,10 @@ export class MigrationGenerator {
|
|
|
759
708
|
this.writer.write(`, ALTER COLUMN "${name}" DROP NOT NULL`);
|
|
760
709
|
}
|
|
761
710
|
}
|
|
762
|
-
this.writer.write(`, ALTER COLUMN "${name}" SET EXPRESSION AS (${field.generateAs
|
|
711
|
+
this.writer.write(`, ALTER COLUMN "${name}" SET EXPRESSION AS (${field.generateAs})`);
|
|
763
712
|
} else {
|
|
764
713
|
this.writer.write(
|
|
765
|
-
`${alter ? 'ALTER' : 'ADD'} COLUMN "${name}" ${type}${nonNull() ? ' not null' : ''} GENERATED ALWAYS AS (${field.generateAs
|
|
714
|
+
`${alter ? 'ALTER' : 'ADD'} COLUMN "${name}" ${type}${nonNull() ? ' not null' : ''} GENERATED ALWAYS AS (${field.generateAs}) STORED`,
|
|
766
715
|
);
|
|
767
716
|
}
|
|
768
717
|
|
|
@@ -795,10 +744,6 @@ export class MigrationGenerator {
|
|
|
795
744
|
};
|
|
796
745
|
const kind = field.kind;
|
|
797
746
|
if (field.generateAs) {
|
|
798
|
-
if (field.generateAs.type === 'expression') {
|
|
799
|
-
throw new Error(`Expression fields cannot be created in SQL schema.`);
|
|
800
|
-
}
|
|
801
|
-
|
|
802
747
|
let type = '';
|
|
803
748
|
switch (kind) {
|
|
804
749
|
case undefined:
|
|
@@ -807,9 +752,6 @@ export class MigrationGenerator {
|
|
|
807
752
|
case 'Float':
|
|
808
753
|
type = `decimal(${field.precision ?? 'undefined'}, ${field.scale ?? 'undefined'})`;
|
|
809
754
|
break;
|
|
810
|
-
case 'Boolean':
|
|
811
|
-
type = 'boolean';
|
|
812
|
-
break;
|
|
813
755
|
default:
|
|
814
756
|
throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
|
|
815
757
|
}
|
|
@@ -818,7 +760,7 @@ export class MigrationGenerator {
|
|
|
818
760
|
throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
|
|
819
761
|
}
|
|
820
762
|
this.writer.write(
|
|
821
|
-
`table.specificType('${name}', '${type}${nonNull() ? ' not null' : ''} GENERATED ALWAYS AS (${field.generateAs
|
|
763
|
+
`table.specificType('${name}', '${type}${nonNull() ? ' not null' : ''} GENERATED ALWAYS AS (${field.generateAs}) STORED')`,
|
|
822
764
|
);
|
|
823
765
|
if (alter) {
|
|
824
766
|
this.writer.write('.alter()');
|
|
@@ -923,19 +865,15 @@ export class MigrationGenerator {
|
|
|
923
865
|
}
|
|
924
866
|
|
|
925
867
|
private hasChanged(model: EntityModel, field: EntityField) {
|
|
926
|
-
if (field.generateAs?.type === 'expression') {
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
868
|
const col = this.getColumn(model.name, field.kind === 'relation' ? `${field.name}Id` : field.name);
|
|
931
869
|
if (!col) {
|
|
932
870
|
return false;
|
|
933
871
|
}
|
|
934
872
|
|
|
935
873
|
if (field.generateAs) {
|
|
936
|
-
if (col.generation_expression !== field.generateAs
|
|
874
|
+
if (col.generation_expression !== field.generateAs) {
|
|
937
875
|
throw new Error(
|
|
938
|
-
`Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs
|
|
876
|
+
`Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs}`,
|
|
939
877
|
);
|
|
940
878
|
}
|
|
941
879
|
}
|
|
@@ -980,237 +918,6 @@ export class MigrationGenerator {
|
|
|
980
918
|
|
|
981
919
|
return false;
|
|
982
920
|
}
|
|
983
|
-
|
|
984
|
-
private normalizeFunctionBody(body: string): string {
|
|
985
|
-
return body
|
|
986
|
-
.replace(/\s+/g, ' ')
|
|
987
|
-
.replace(/\s*\(\s*/g, '(')
|
|
988
|
-
.replace(/\s*\)\s*/g, ')')
|
|
989
|
-
.replace(/\s*,\s*/g, ',')
|
|
990
|
-
.trim();
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
private normalizeAggregateDefinition(definition: string): string {
|
|
994
|
-
let normalized = definition
|
|
995
|
-
.replace(/\s+/g, ' ')
|
|
996
|
-
.replace(/\s*\(\s*/g, '(')
|
|
997
|
-
.replace(/\s*\)\s*/g, ')')
|
|
998
|
-
.replace(/\s*,\s*/g, ',')
|
|
999
|
-
.trim();
|
|
1000
|
-
|
|
1001
|
-
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
1002
|
-
if (initCondMatch) {
|
|
1003
|
-
const initCondValue = initCondMatch[1].trim();
|
|
1004
|
-
const unquoted = initCondValue.replace(/^['"]|['"]$/g, '');
|
|
1005
|
-
if (/^\d+$/.test(unquoted)) {
|
|
1006
|
-
normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
return normalized;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
private extractFunctionBody(definition: string): string {
|
|
1014
|
-
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
1015
|
-
if (dollarQuoteMatch) {
|
|
1016
|
-
return dollarQuoteMatch[2].trim();
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
1020
|
-
if (bodyMatch) {
|
|
1021
|
-
return bodyMatch[1].trim();
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
return definition;
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
private async getDatabaseFunctions(): Promise<DatabaseFunction[]> {
|
|
1028
|
-
const regularFunctions = await this.knex.raw(`
|
|
1029
|
-
SELECT
|
|
1030
|
-
p.proname as name,
|
|
1031
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1032
|
-
pg_get_functiondef(p.oid) as definition,
|
|
1033
|
-
false as is_aggregate
|
|
1034
|
-
FROM pg_proc p
|
|
1035
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1036
|
-
WHERE n.nspname = 'public'
|
|
1037
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
1038
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1039
|
-
`);
|
|
1040
|
-
|
|
1041
|
-
const aggregateFunctions = await this.knex.raw(`
|
|
1042
|
-
SELECT
|
|
1043
|
-
p.proname as name,
|
|
1044
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1045
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
1046
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
1047
|
-
a.agginitval as init_val,
|
|
1048
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
|
|
1049
|
-
true as is_aggregate
|
|
1050
|
-
FROM pg_proc p
|
|
1051
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
1052
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1053
|
-
WHERE n.nspname = 'public'
|
|
1054
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1055
|
-
`);
|
|
1056
|
-
|
|
1057
|
-
const result: DatabaseFunction[] = [];
|
|
1058
|
-
|
|
1059
|
-
for (const row of regularFunctions.rows || []) {
|
|
1060
|
-
const definition = row.definition || '';
|
|
1061
|
-
const name = row.name || '';
|
|
1062
|
-
const argumentsStr = row.arguments || '';
|
|
1063
|
-
|
|
1064
|
-
if (!definition) {
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const signature = `${name}(${argumentsStr})`;
|
|
1069
|
-
const body = this.normalizeFunctionBody(this.extractFunctionBody(definition));
|
|
1070
|
-
|
|
1071
|
-
result.push({
|
|
1072
|
-
name,
|
|
1073
|
-
signature,
|
|
1074
|
-
body,
|
|
1075
|
-
isAggregate: false,
|
|
1076
|
-
definition,
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
1081
|
-
const name = row.name || '';
|
|
1082
|
-
const argumentsStr = row.arguments || '';
|
|
1083
|
-
const transFunc = row.trans_func || '';
|
|
1084
|
-
const finalFunc = row.final_func || '';
|
|
1085
|
-
const initVal = row.init_val;
|
|
1086
|
-
const stateType = row.state_type || '';
|
|
1087
|
-
|
|
1088
|
-
const signature = `${name}(${argumentsStr})`;
|
|
1089
|
-
|
|
1090
|
-
let aggregateDef = `CREATE AGGREGATE ${name}(${argumentsStr}) (`;
|
|
1091
|
-
aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
|
|
1092
|
-
|
|
1093
|
-
if (finalFunc) {
|
|
1094
|
-
aggregateDef += `, FINALFUNC = ${finalFunc}`;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
if (initVal !== null && initVal !== undefined) {
|
|
1098
|
-
let initValStr: string;
|
|
1099
|
-
if (typeof initVal === 'string') {
|
|
1100
|
-
initValStr = `'${initVal}'`;
|
|
1101
|
-
} else {
|
|
1102
|
-
const numStr = String(initVal);
|
|
1103
|
-
initValStr = /^\d+$/.test(numStr) ? `'${numStr}'` : numStr;
|
|
1104
|
-
}
|
|
1105
|
-
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
aggregateDef += ');';
|
|
1109
|
-
|
|
1110
|
-
result.push({
|
|
1111
|
-
name,
|
|
1112
|
-
signature,
|
|
1113
|
-
body: this.normalizeAggregateDefinition(aggregateDef),
|
|
1114
|
-
isAggregate: true,
|
|
1115
|
-
definition: aggregateDef,
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
return result;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
private async handleFunctions(up: Callbacks, down: Callbacks) {
|
|
1123
|
-
if (!this.parsedFunctions || this.parsedFunctions.length === 0) {
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
const definedFunctions = this.parsedFunctions;
|
|
1128
|
-
|
|
1129
|
-
const dbFunctions = await this.getDatabaseFunctions();
|
|
1130
|
-
const dbFunctionsBySignature = new Map<string, DatabaseFunction>();
|
|
1131
|
-
for (const func of dbFunctions) {
|
|
1132
|
-
dbFunctionsBySignature.set(func.signature, func);
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
const definedFunctionsBySignature = new Map<string, ParsedFunction>();
|
|
1136
|
-
for (const func of definedFunctions) {
|
|
1137
|
-
definedFunctionsBySignature.set(func.signature, func);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
const functionsToRestore: { func: DatabaseFunction; definition: string }[] = [];
|
|
1141
|
-
|
|
1142
|
-
for (const definedFunc of definedFunctions) {
|
|
1143
|
-
const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
|
|
1144
|
-
|
|
1145
|
-
if (!dbFunc) {
|
|
1146
|
-
up.push(() => {
|
|
1147
|
-
this.writer.writeLine(`await knex.raw(\`${definedFunc.fullDefinition.replace(/`/g, '\\`')}\`);`).blankLine();
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
down.push(() => {
|
|
1151
|
-
const isAggregate = definedFunc.isAggregate;
|
|
1152
|
-
const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?(FUNCTION|AGGREGATE)\s+([^(]+)\(/i);
|
|
1153
|
-
if (dropMatch) {
|
|
1154
|
-
const functionName = dropMatch[3].trim();
|
|
1155
|
-
const argsMatch = definedFunc.fullDefinition.match(
|
|
1156
|
-
/CREATE\s+(OR\s+REPLACE\s+)?(FUNCTION|AGGREGATE)\s+[^(]+\(([^)]*)\)/i,
|
|
1157
|
-
);
|
|
1158
|
-
const args = argsMatch ? argsMatch[3].trim() : '';
|
|
1159
|
-
const dropType = isAggregate ? 'AGGREGATE' : 'FUNCTION';
|
|
1160
|
-
this.writer
|
|
1161
|
-
.writeLine(`await knex.raw(\`DROP ${dropType} IF EXISTS ${functionName}${args ? `(${args})` : ''}\`);`)
|
|
1162
|
-
.blankLine();
|
|
1163
|
-
}
|
|
1164
|
-
});
|
|
1165
|
-
} else {
|
|
1166
|
-
const dbBody = dbFunc.isAggregate
|
|
1167
|
-
? this.normalizeAggregateDefinition(dbFunc.body)
|
|
1168
|
-
: this.normalizeFunctionBody(dbFunc.body);
|
|
1169
|
-
const definedBody = definedFunc.isAggregate
|
|
1170
|
-
? this.normalizeAggregateDefinition(definedFunc.body)
|
|
1171
|
-
: this.normalizeFunctionBody(definedFunc.body);
|
|
1172
|
-
|
|
1173
|
-
if (dbBody !== definedBody) {
|
|
1174
|
-
const oldDefinition = dbFunc.definition || dbFunc.body;
|
|
1175
|
-
|
|
1176
|
-
up.push(() => {
|
|
1177
|
-
this.writer.writeLine(`await knex.raw(\`${definedFunc.fullDefinition.replace(/`/g, '\\`')}\`);`).blankLine();
|
|
1178
|
-
});
|
|
1179
|
-
|
|
1180
|
-
down.push(() => {
|
|
1181
|
-
if (oldDefinition) {
|
|
1182
|
-
this.writer.writeLine(`await knex.raw(\`${oldDefinition.replace(/`/g, '\\`')}\`);`).blankLine();
|
|
1183
|
-
}
|
|
1184
|
-
});
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
for (const dbFunc of dbFunctions) {
|
|
1190
|
-
if (!definedFunctionsBySignature.has(dbFunc.signature)) {
|
|
1191
|
-
const definition = dbFunc.definition || dbFunc.body;
|
|
1192
|
-
|
|
1193
|
-
if (definition) {
|
|
1194
|
-
functionsToRestore.push({ func: dbFunc, definition });
|
|
1195
|
-
|
|
1196
|
-
down.push(() => {
|
|
1197
|
-
const argsMatch = dbFunc.signature.match(/\(([^)]*)\)/);
|
|
1198
|
-
const args = argsMatch ? argsMatch[1] : '';
|
|
1199
|
-
const dropType = dbFunc.isAggregate ? 'AGGREGATE' : 'FUNCTION';
|
|
1200
|
-
this.writer
|
|
1201
|
-
.writeLine(`await knex.raw(\`DROP ${dropType} IF EXISTS ${dbFunc.name}${args ? `(${args})` : ''}\`);`)
|
|
1202
|
-
.blankLine();
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
for (const { definition } of functionsToRestore) {
|
|
1209
|
-
up.push(() => {
|
|
1210
|
-
this.writer.writeLine(`await knex.raw(\`${definition.replace(/`/g, '\\`')}\`);`).blankLine();
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
921
|
}
|
|
1215
922
|
|
|
1216
923
|
export const getMigrationDate = () => {
|
package/src/migrations/index.ts
CHANGED
|
@@ -90,10 +90,7 @@ export type EntityFieldDefinition = FieldDefinitionBase &
|
|
|
90
90
|
indent?: boolean;
|
|
91
91
|
// If true the field is hidden in the admin interface
|
|
92
92
|
hidden?: boolean;
|
|
93
|
-
generateAs?:
|
|
94
|
-
expression: string;
|
|
95
|
-
type: 'virtual' | 'stored' | 'expression';
|
|
96
|
-
};
|
|
93
|
+
generateAs?: string;
|
|
97
94
|
|
|
98
95
|
// Temporary fields for the generation of migrations
|
|
99
96
|
deleted?: true;
|