@smartive/graphql-magic 23.4.0-next.8 → 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 -2
- package/dist/bin/gqm.cjs +125 -656
- package/dist/cjs/index.cjs +2132 -2702
- 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 -73
- package/dist/esm/resolvers/filters.js.map +1 -1
- package/dist/esm/resolvers/selects.js +2 -33
- 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 -22
- 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 -81
- package/src/resolvers/selects.ts +5 -38
- package/src/resolvers/utils.ts +0 -32
- 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
package/dist/bin/gqm.cjs
CHANGED
|
@@ -750,25 +750,9 @@ var generateKnexTables = (models) => {
|
|
|
750
750
|
var import_code_block_writer2 = __toESM(require("code-block-writer"), 1);
|
|
751
751
|
var import_knex_schema_inspector = require("knex-schema-inspector");
|
|
752
752
|
var import_lowerFirst = __toESM(require("lodash/lowerFirst"), 1);
|
|
753
|
-
|
|
754
|
-
// src/resolvers/arguments.ts
|
|
755
|
-
var import_graphql3 = require("graphql");
|
|
756
|
-
|
|
757
|
-
// src/resolvers/utils.ts
|
|
758
|
-
var import_graphql4 = require("graphql");
|
|
759
|
-
var import_isEqual = __toESM(require("lodash/isEqual"), 1);
|
|
760
|
-
var getColumnName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
|
|
761
|
-
|
|
762
|
-
// src/resolvers/resolver.ts
|
|
763
|
-
var import_cloneDeep2 = __toESM(require("lodash/cloneDeep"), 1);
|
|
764
|
-
var import_flatMap = __toESM(require("lodash/flatMap"), 1);
|
|
765
|
-
|
|
766
|
-
// src/migrations/generate.ts
|
|
767
753
|
var MigrationGenerator = class {
|
|
768
|
-
constructor(knex2, models
|
|
754
|
+
constructor(knex2, models) {
|
|
769
755
|
this.models = models;
|
|
770
|
-
this.parsedFunctions = parsedFunctions;
|
|
771
|
-
this.knex = knex2;
|
|
772
756
|
this.schema = (0, import_knex_schema_inspector.SchemaInspector)(knex2);
|
|
773
757
|
}
|
|
774
758
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
@@ -781,7 +765,6 @@ var MigrationGenerator = class {
|
|
|
781
765
|
uuidUsed;
|
|
782
766
|
nowUsed;
|
|
783
767
|
needsMigration = false;
|
|
784
|
-
knex;
|
|
785
768
|
async generate() {
|
|
786
769
|
const { writer, schema, models } = this;
|
|
787
770
|
const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
|
|
@@ -796,7 +779,6 @@ var MigrationGenerator = class {
|
|
|
796
779
|
up,
|
|
797
780
|
down
|
|
798
781
|
);
|
|
799
|
-
await this.handleFunctions(up, down);
|
|
800
782
|
for (const model of models.entities) {
|
|
801
783
|
if (model.deleted) {
|
|
802
784
|
up.push(() => {
|
|
@@ -858,7 +840,7 @@ var MigrationGenerator = class {
|
|
|
858
840
|
foreignKey: "id"
|
|
859
841
|
});
|
|
860
842
|
}
|
|
861
|
-
for (const field of model.fields.filter(not(isInherited))
|
|
843
|
+
for (const field of model.fields.filter(not(isInherited))) {
|
|
862
844
|
this.column(field);
|
|
863
845
|
}
|
|
864
846
|
});
|
|
@@ -867,25 +849,29 @@ var MigrationGenerator = class {
|
|
|
867
849
|
this.dropTable(model.name);
|
|
868
850
|
});
|
|
869
851
|
} else {
|
|
870
|
-
|
|
871
|
-
|
|
852
|
+
this.renameFields(
|
|
853
|
+
model,
|
|
854
|
+
model.fields.filter(not(isInherited)).filter(({ oldName }) => oldName),
|
|
855
|
+
up,
|
|
856
|
+
down
|
|
857
|
+
);
|
|
872
858
|
this.createFields(
|
|
873
859
|
model,
|
|
874
860
|
model.fields.filter(not(isInherited)).filter(
|
|
875
|
-
({ name: name2, ...field }) => field.kind !== "custom" && !
|
|
861
|
+
({ name: name2, ...field }) => field.kind !== "custom" && !this.getColumn(model.name, field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
|
|
876
862
|
),
|
|
877
863
|
up,
|
|
878
864
|
down
|
|
879
865
|
);
|
|
880
866
|
const rawExistingFields = model.fields.filter((field) => {
|
|
881
|
-
if (!field.generateAs
|
|
867
|
+
if (!field.generateAs) {
|
|
882
868
|
return false;
|
|
883
869
|
}
|
|
884
870
|
const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
|
|
885
871
|
if (!col) {
|
|
886
872
|
return false;
|
|
887
873
|
}
|
|
888
|
-
if (col.generation_expression !== field.generateAs
|
|
874
|
+
if (col.generation_expression !== field.generateAs) {
|
|
889
875
|
return true;
|
|
890
876
|
}
|
|
891
877
|
return this.hasChanged(model, field);
|
|
@@ -893,9 +879,7 @@ var MigrationGenerator = class {
|
|
|
893
879
|
if (rawExistingFields.length) {
|
|
894
880
|
this.updateFieldsRaw(model, rawExistingFields, up, down);
|
|
895
881
|
}
|
|
896
|
-
const existingFields = model.fields.filter(
|
|
897
|
-
(field) => (!field.generateAs || field.generateAs.type === "expression") && this.hasChanged(model, field)
|
|
898
|
-
);
|
|
882
|
+
const existingFields = model.fields.filter((field) => !field.generateAs && this.hasChanged(model, field));
|
|
899
883
|
this.updateFields(model, existingFields, up, down);
|
|
900
884
|
}
|
|
901
885
|
if (isUpdatableModel(model)) {
|
|
@@ -919,7 +903,7 @@ var MigrationGenerator = class {
|
|
|
919
903
|
writer.writeLine(`deleteRootType: row.deleteRootType,`);
|
|
920
904
|
writer.writeLine(`deleteRootId: row.deleteRootId,`);
|
|
921
905
|
}
|
|
922
|
-
for (const { name: name2, kind } of model.fields.filter(isUpdatableField)
|
|
906
|
+
for (const { name: name2, kind } of model.fields.filter(isUpdatableField)) {
|
|
923
907
|
const col = kind === "relation" ? `${name2}Id` : name2;
|
|
924
908
|
writer.writeLine(`${col}: row.${col},`);
|
|
925
909
|
}
|
|
@@ -933,14 +917,8 @@ var MigrationGenerator = class {
|
|
|
933
917
|
});
|
|
934
918
|
} else {
|
|
935
919
|
const revisionTable = `${model.name}Revision`;
|
|
936
|
-
this.renameFields(
|
|
937
|
-
revisionTable,
|
|
938
|
-
model.fields.filter(isUpdatableField).filter(not(isInherited)).filter(({ oldName }) => oldName),
|
|
939
|
-
up,
|
|
940
|
-
down
|
|
941
|
-
);
|
|
942
920
|
const missingRevisionFields = model.fields.filter(isUpdatableField).filter(
|
|
943
|
-
({ name: name2, ...field }) => field.kind !== "custom" && !
|
|
921
|
+
({ name: name2, ...field }) => field.kind !== "custom" && !this.getColumn(revisionTable, field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
|
|
944
922
|
);
|
|
945
923
|
this.createRevisionFields(model, missingRevisionFields, up, down);
|
|
946
924
|
const revisionFieldsToRemove = model.fields.filter(
|
|
@@ -1006,13 +984,13 @@ var MigrationGenerator = class {
|
|
|
1006
984
|
this.migration("down", down.reverse());
|
|
1007
985
|
return writer.toString();
|
|
1008
986
|
}
|
|
1009
|
-
renameFields(
|
|
987
|
+
renameFields(model, fields2, up, down) {
|
|
1010
988
|
if (!fields2.length) {
|
|
1011
989
|
return;
|
|
1012
990
|
}
|
|
1013
991
|
up.push(() => {
|
|
1014
992
|
for (const field of fields2) {
|
|
1015
|
-
this.alterTable(
|
|
993
|
+
this.alterTable(model.name, () => {
|
|
1016
994
|
this.renameColumn(
|
|
1017
995
|
field.kind === "relation" ? `${field.oldName}Id` : get(field, "oldName"),
|
|
1018
996
|
field.kind === "relation" ? `${field.name}Id` : field.name
|
|
@@ -1022,7 +1000,7 @@ var MigrationGenerator = class {
|
|
|
1022
1000
|
});
|
|
1023
1001
|
down.push(() => {
|
|
1024
1002
|
for (const field of fields2) {
|
|
1025
|
-
this.alterTable(
|
|
1003
|
+
this.alterTable(model.name, () => {
|
|
1026
1004
|
this.renameColumn(
|
|
1027
1005
|
field.kind === "relation" ? `${field.name}Id` : field.name,
|
|
1028
1006
|
field.kind === "relation" ? `${field.oldName}Id` : get(field, "oldName")
|
|
@@ -1031,7 +1009,7 @@ var MigrationGenerator = class {
|
|
|
1031
1009
|
}
|
|
1032
1010
|
});
|
|
1033
1011
|
for (const field of fields2) {
|
|
1034
|
-
summonByName(this.columns[
|
|
1012
|
+
summonByName(this.columns[model.name], field.kind === "relation" ? `${field.oldName}Id` : field.oldName).name = field.kind === "relation" ? `${field.name}Id` : field.name;
|
|
1035
1013
|
}
|
|
1036
1014
|
}
|
|
1037
1015
|
createFields(model, fields2, up, down) {
|
|
@@ -1043,9 +1021,6 @@ var MigrationGenerator = class {
|
|
|
1043
1021
|
const updates = [];
|
|
1044
1022
|
const postAlter = [];
|
|
1045
1023
|
for (const field of fields2) {
|
|
1046
|
-
if (field.generateAs?.type === "expression") {
|
|
1047
|
-
continue;
|
|
1048
|
-
}
|
|
1049
1024
|
alter.push(() => this.column(field, { setNonNull: field.defaultValue !== void 0 }));
|
|
1050
1025
|
if (field.generateAs) {
|
|
1051
1026
|
continue;
|
|
@@ -1098,7 +1073,7 @@ var MigrationGenerator = class {
|
|
|
1098
1073
|
});
|
|
1099
1074
|
});
|
|
1100
1075
|
if (isUpdatableModel(model)) {
|
|
1101
|
-
const updatableFields = fields2.filter(isUpdatableField)
|
|
1076
|
+
const updatableFields = fields2.filter(isUpdatableField);
|
|
1102
1077
|
if (!updatableFields.length) {
|
|
1103
1078
|
return;
|
|
1104
1079
|
}
|
|
@@ -1146,7 +1121,7 @@ var MigrationGenerator = class {
|
|
|
1146
1121
|
});
|
|
1147
1122
|
});
|
|
1148
1123
|
if (isUpdatableModel(model)) {
|
|
1149
|
-
const updatableFields = fields2.filter(isUpdatableField)
|
|
1124
|
+
const updatableFields = fields2.filter(isUpdatableField);
|
|
1150
1125
|
if (!updatableFields.length) {
|
|
1151
1126
|
return;
|
|
1152
1127
|
}
|
|
@@ -1184,7 +1159,7 @@ var MigrationGenerator = class {
|
|
|
1184
1159
|
writer.writeLine(`table.uuid('deleteRootId');`);
|
|
1185
1160
|
}
|
|
1186
1161
|
}
|
|
1187
|
-
for (const field of model.fields.filter(and(isUpdatableField, not(isInherited)))
|
|
1162
|
+
for (const field of model.fields.filter(and(isUpdatableField, not(isInherited)))) {
|
|
1188
1163
|
this.column(field, { setUnique: false, setDefault: false });
|
|
1189
1164
|
}
|
|
1190
1165
|
});
|
|
@@ -1198,19 +1173,14 @@ var MigrationGenerator = class {
|
|
|
1198
1173
|
this.column(field, { setUnique: false, setNonNull: false, setDefault: false });
|
|
1199
1174
|
}
|
|
1200
1175
|
});
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
`${col}: knex.raw('(select "${col}" from "${model.name}" where "${model.name}".id = "${model.name}Revision"."${typeToField(model.name)}Id")'),`
|
|
1210
|
-
).newLine();
|
|
1211
|
-
}
|
|
1212
|
-
}).write(");").newLine().blankLine();
|
|
1213
|
-
}
|
|
1176
|
+
this.writer.write(`await knex('${model.name}Revision').update(`).inlineBlock(() => {
|
|
1177
|
+
for (const { name: name2, kind: type } of missingRevisionFields) {
|
|
1178
|
+
const col = type === "relation" ? `${name2}Id` : name2;
|
|
1179
|
+
this.writer.write(
|
|
1180
|
+
`${col}: knex.raw('(select "${col}" from "${model.name}" where "${model.name}".id = "${model.name}Revision"."${typeToField(model.name)}Id")'),`
|
|
1181
|
+
).newLine();
|
|
1182
|
+
}
|
|
1183
|
+
}).write(");").newLine().blankLine();
|
|
1214
1184
|
const nonNullableMissingRevisionFields = missingRevisionFields.filter(({ nonNull: nonNull2 }) => nonNull2);
|
|
1215
1185
|
if (nonNullableMissingRevisionFields.length) {
|
|
1216
1186
|
this.alterTable(revisionTable, () => {
|
|
@@ -1266,7 +1236,7 @@ var MigrationGenerator = class {
|
|
|
1266
1236
|
return this.writer.writeLine(`await knex.schema.renameTable('${from}', '${to}');`).blankLine();
|
|
1267
1237
|
}
|
|
1268
1238
|
renameColumn(from, to) {
|
|
1269
|
-
this.writer.writeLine(`table.renameColumn('${from}', '${to}')
|
|
1239
|
+
this.writer.writeLine(`table.renameColumn('${from}', '${to}')`);
|
|
1270
1240
|
}
|
|
1271
1241
|
value(value2) {
|
|
1272
1242
|
if (typeof value2 === "string") {
|
|
@@ -1291,9 +1261,6 @@ var MigrationGenerator = class {
|
|
|
1291
1261
|
};
|
|
1292
1262
|
const kind = field.kind;
|
|
1293
1263
|
if (field.generateAs) {
|
|
1294
|
-
if (field.generateAs.type === "expression") {
|
|
1295
|
-
throw new Error(`Expression fields cannot be created in SQL schema.`);
|
|
1296
|
-
}
|
|
1297
1264
|
let type = "";
|
|
1298
1265
|
switch (kind) {
|
|
1299
1266
|
case void 0:
|
|
@@ -1302,9 +1269,6 @@ var MigrationGenerator = class {
|
|
|
1302
1269
|
case "Float":
|
|
1303
1270
|
type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
|
|
1304
1271
|
break;
|
|
1305
|
-
case "Boolean":
|
|
1306
|
-
type = "boolean";
|
|
1307
|
-
break;
|
|
1308
1272
|
default:
|
|
1309
1273
|
throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
|
|
1310
1274
|
}
|
|
@@ -1324,10 +1288,10 @@ var MigrationGenerator = class {
|
|
|
1324
1288
|
this.writer.write(`, ALTER COLUMN "${name2}" DROP NOT NULL`);
|
|
1325
1289
|
}
|
|
1326
1290
|
}
|
|
1327
|
-
this.writer.write(`, ALTER COLUMN "${name2}" SET EXPRESSION AS (${field.generateAs
|
|
1291
|
+
this.writer.write(`, ALTER COLUMN "${name2}" SET EXPRESSION AS (${field.generateAs})`);
|
|
1328
1292
|
} else {
|
|
1329
1293
|
this.writer.write(
|
|
1330
|
-
`${alter ? "ALTER" : "ADD"} COLUMN "${name2}" ${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs
|
|
1294
|
+
`${alter ? "ALTER" : "ADD"} COLUMN "${name2}" ${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED`
|
|
1331
1295
|
);
|
|
1332
1296
|
}
|
|
1333
1297
|
return;
|
|
@@ -1351,9 +1315,6 @@ var MigrationGenerator = class {
|
|
|
1351
1315
|
};
|
|
1352
1316
|
const kind = field.kind;
|
|
1353
1317
|
if (field.generateAs) {
|
|
1354
|
-
if (field.generateAs.type === "expression") {
|
|
1355
|
-
throw new Error(`Expression fields cannot be created in SQL schema.`);
|
|
1356
|
-
}
|
|
1357
1318
|
let type = "";
|
|
1358
1319
|
switch (kind) {
|
|
1359
1320
|
case void 0:
|
|
@@ -1362,9 +1323,6 @@ var MigrationGenerator = class {
|
|
|
1362
1323
|
case "Float":
|
|
1363
1324
|
type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
|
|
1364
1325
|
break;
|
|
1365
|
-
case "Boolean":
|
|
1366
|
-
type = "boolean";
|
|
1367
|
-
break;
|
|
1368
1326
|
default:
|
|
1369
1327
|
throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
|
|
1370
1328
|
}
|
|
@@ -1373,7 +1331,7 @@ var MigrationGenerator = class {
|
|
|
1373
1331
|
throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
|
|
1374
1332
|
}
|
|
1375
1333
|
this.writer.write(
|
|
1376
|
-
`table.specificType('${name2}', '${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs
|
|
1334
|
+
`table.specificType('${name2}', '${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED')`
|
|
1377
1335
|
);
|
|
1378
1336
|
if (alter) {
|
|
1379
1337
|
this.writer.write(".alter()");
|
|
@@ -1471,17 +1429,14 @@ var MigrationGenerator = class {
|
|
|
1471
1429
|
return this.columns[tableName].find((col) => col.name === columnName);
|
|
1472
1430
|
}
|
|
1473
1431
|
hasChanged(model, field) {
|
|
1474
|
-
if (field.generateAs?.type === "expression") {
|
|
1475
|
-
return false;
|
|
1476
|
-
}
|
|
1477
1432
|
const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
|
|
1478
1433
|
if (!col) {
|
|
1479
1434
|
return false;
|
|
1480
1435
|
}
|
|
1481
1436
|
if (field.generateAs) {
|
|
1482
|
-
if (col.generation_expression !== field.generateAs
|
|
1437
|
+
if (col.generation_expression !== field.generateAs) {
|
|
1483
1438
|
throw new Error(
|
|
1484
|
-
`Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs
|
|
1439
|
+
`Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs}`
|
|
1485
1440
|
);
|
|
1486
1441
|
}
|
|
1487
1442
|
}
|
|
@@ -1523,182 +1478,6 @@ var MigrationGenerator = class {
|
|
|
1523
1478
|
}
|
|
1524
1479
|
return false;
|
|
1525
1480
|
}
|
|
1526
|
-
normalizeFunctionBody(body) {
|
|
1527
|
-
return body.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").trim();
|
|
1528
|
-
}
|
|
1529
|
-
normalizeAggregateDefinition(definition) {
|
|
1530
|
-
let normalized = definition.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").trim();
|
|
1531
|
-
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
1532
|
-
if (initCondMatch) {
|
|
1533
|
-
const initCondValue = initCondMatch[1].trim();
|
|
1534
|
-
const unquoted = initCondValue.replace(/^['"]|['"]$/g, "");
|
|
1535
|
-
if (/^\d+$/.test(unquoted)) {
|
|
1536
|
-
normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return normalized;
|
|
1540
|
-
}
|
|
1541
|
-
extractFunctionBody(definition) {
|
|
1542
|
-
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
1543
|
-
if (dollarQuoteMatch) {
|
|
1544
|
-
return dollarQuoteMatch[2].trim();
|
|
1545
|
-
}
|
|
1546
|
-
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
1547
|
-
if (bodyMatch) {
|
|
1548
|
-
return bodyMatch[1].trim();
|
|
1549
|
-
}
|
|
1550
|
-
return definition;
|
|
1551
|
-
}
|
|
1552
|
-
async getDatabaseFunctions() {
|
|
1553
|
-
const regularFunctions = await this.knex.raw(`
|
|
1554
|
-
SELECT
|
|
1555
|
-
p.proname as name,
|
|
1556
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1557
|
-
pg_get_functiondef(p.oid) as definition,
|
|
1558
|
-
false as is_aggregate
|
|
1559
|
-
FROM pg_proc p
|
|
1560
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1561
|
-
WHERE n.nspname = 'public'
|
|
1562
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
1563
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1564
|
-
`);
|
|
1565
|
-
const aggregateFunctions = await this.knex.raw(`
|
|
1566
|
-
SELECT
|
|
1567
|
-
p.proname as name,
|
|
1568
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1569
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
1570
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
1571
|
-
a.agginitval as init_val,
|
|
1572
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
|
|
1573
|
-
true as is_aggregate
|
|
1574
|
-
FROM pg_proc p
|
|
1575
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
1576
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1577
|
-
WHERE n.nspname = 'public'
|
|
1578
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1579
|
-
`);
|
|
1580
|
-
const result = [];
|
|
1581
|
-
for (const row of regularFunctions.rows || []) {
|
|
1582
|
-
const definition = row.definition || "";
|
|
1583
|
-
const name2 = row.name || "";
|
|
1584
|
-
const argumentsStr = row.arguments || "";
|
|
1585
|
-
if (!definition) {
|
|
1586
|
-
continue;
|
|
1587
|
-
}
|
|
1588
|
-
const signature = `${name2}(${argumentsStr})`;
|
|
1589
|
-
const body = this.normalizeFunctionBody(this.extractFunctionBody(definition));
|
|
1590
|
-
result.push({
|
|
1591
|
-
name: name2,
|
|
1592
|
-
signature,
|
|
1593
|
-
body,
|
|
1594
|
-
isAggregate: false,
|
|
1595
|
-
definition
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
1599
|
-
const name2 = row.name || "";
|
|
1600
|
-
const argumentsStr = row.arguments || "";
|
|
1601
|
-
const transFunc = row.trans_func || "";
|
|
1602
|
-
const finalFunc = row.final_func || "";
|
|
1603
|
-
const initVal = row.init_val;
|
|
1604
|
-
const stateType = row.state_type || "";
|
|
1605
|
-
const signature = `${name2}(${argumentsStr})`;
|
|
1606
|
-
let aggregateDef = `CREATE AGGREGATE ${name2}(${argumentsStr}) (`;
|
|
1607
|
-
aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
|
|
1608
|
-
if (finalFunc) {
|
|
1609
|
-
aggregateDef += `, FINALFUNC = ${finalFunc}`;
|
|
1610
|
-
}
|
|
1611
|
-
if (initVal !== null && initVal !== void 0) {
|
|
1612
|
-
let initValStr;
|
|
1613
|
-
if (typeof initVal === "string") {
|
|
1614
|
-
initValStr = `'${initVal}'`;
|
|
1615
|
-
} else {
|
|
1616
|
-
const numStr = String(initVal);
|
|
1617
|
-
initValStr = /^\d+$/.test(numStr) ? `'${numStr}'` : numStr;
|
|
1618
|
-
}
|
|
1619
|
-
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
1620
|
-
}
|
|
1621
|
-
aggregateDef += ");";
|
|
1622
|
-
result.push({
|
|
1623
|
-
name: name2,
|
|
1624
|
-
signature,
|
|
1625
|
-
body: this.normalizeAggregateDefinition(aggregateDef),
|
|
1626
|
-
isAggregate: true,
|
|
1627
|
-
definition: aggregateDef
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
return result;
|
|
1631
|
-
}
|
|
1632
|
-
async handleFunctions(up, down) {
|
|
1633
|
-
if (!this.parsedFunctions || this.parsedFunctions.length === 0) {
|
|
1634
|
-
return;
|
|
1635
|
-
}
|
|
1636
|
-
const definedFunctions = this.parsedFunctions;
|
|
1637
|
-
const dbFunctions = await this.getDatabaseFunctions();
|
|
1638
|
-
const dbFunctionsBySignature = /* @__PURE__ */ new Map();
|
|
1639
|
-
for (const func of dbFunctions) {
|
|
1640
|
-
dbFunctionsBySignature.set(func.signature, func);
|
|
1641
|
-
}
|
|
1642
|
-
const definedFunctionsBySignature = /* @__PURE__ */ new Map();
|
|
1643
|
-
for (const func of definedFunctions) {
|
|
1644
|
-
definedFunctionsBySignature.set(func.signature, func);
|
|
1645
|
-
}
|
|
1646
|
-
const functionsToRestore = [];
|
|
1647
|
-
for (const definedFunc of definedFunctions) {
|
|
1648
|
-
const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
|
|
1649
|
-
if (!dbFunc) {
|
|
1650
|
-
up.push(() => {
|
|
1651
|
-
this.writer.writeLine(`await knex.raw(\`${definedFunc.fullDefinition.replace(/`/g, "\\`")}\`);`).blankLine();
|
|
1652
|
-
});
|
|
1653
|
-
down.push(() => {
|
|
1654
|
-
const isAggregate = definedFunc.isAggregate;
|
|
1655
|
-
const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?(FUNCTION|AGGREGATE)\s+([^(]+)\(/i);
|
|
1656
|
-
if (dropMatch) {
|
|
1657
|
-
const functionName = dropMatch[3].trim();
|
|
1658
|
-
const argsMatch = definedFunc.fullDefinition.match(
|
|
1659
|
-
/CREATE\s+(OR\s+REPLACE\s+)?(FUNCTION|AGGREGATE)\s+[^(]+\(([^)]*)\)/i
|
|
1660
|
-
);
|
|
1661
|
-
const args2 = argsMatch ? argsMatch[3].trim() : "";
|
|
1662
|
-
const dropType = isAggregate ? "AGGREGATE" : "FUNCTION";
|
|
1663
|
-
this.writer.writeLine(`await knex.raw(\`DROP ${dropType} IF EXISTS ${functionName}${args2 ? `(${args2})` : ""}\`);`).blankLine();
|
|
1664
|
-
}
|
|
1665
|
-
});
|
|
1666
|
-
} else {
|
|
1667
|
-
const dbBody = dbFunc.isAggregate ? this.normalizeAggregateDefinition(dbFunc.body) : this.normalizeFunctionBody(dbFunc.body);
|
|
1668
|
-
const definedBody = definedFunc.isAggregate ? this.normalizeAggregateDefinition(definedFunc.body) : this.normalizeFunctionBody(definedFunc.body);
|
|
1669
|
-
if (dbBody !== definedBody) {
|
|
1670
|
-
const oldDefinition = dbFunc.definition || dbFunc.body;
|
|
1671
|
-
up.push(() => {
|
|
1672
|
-
this.writer.writeLine(`await knex.raw(\`${definedFunc.fullDefinition.replace(/`/g, "\\`")}\`);`).blankLine();
|
|
1673
|
-
});
|
|
1674
|
-
down.push(() => {
|
|
1675
|
-
if (oldDefinition) {
|
|
1676
|
-
this.writer.writeLine(`await knex.raw(\`${oldDefinition.replace(/`/g, "\\`")}\`);`).blankLine();
|
|
1677
|
-
}
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
for (const dbFunc of dbFunctions) {
|
|
1683
|
-
if (!definedFunctionsBySignature.has(dbFunc.signature)) {
|
|
1684
|
-
const definition = dbFunc.definition || dbFunc.body;
|
|
1685
|
-
if (definition) {
|
|
1686
|
-
functionsToRestore.push({ func: dbFunc, definition });
|
|
1687
|
-
down.push(() => {
|
|
1688
|
-
const argsMatch = dbFunc.signature.match(/\(([^)]*)\)/);
|
|
1689
|
-
const args2 = argsMatch ? argsMatch[1] : "";
|
|
1690
|
-
const dropType = dbFunc.isAggregate ? "AGGREGATE" : "FUNCTION";
|
|
1691
|
-
this.writer.writeLine(`await knex.raw(\`DROP ${dropType} IF EXISTS ${dbFunc.name}${args2 ? `(${args2})` : ""}\`);`).blankLine();
|
|
1692
|
-
});
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
for (const { definition } of functionsToRestore) {
|
|
1697
|
-
up.push(() => {
|
|
1698
|
-
this.writer.writeLine(`await knex.raw(\`${definition.replace(/`/g, "\\`")}\`);`).blankLine();
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
1481
|
};
|
|
1703
1482
|
var getMigrationDate = () => {
|
|
1704
1483
|
const date = /* @__PURE__ */ new Date();
|
|
@@ -1711,258 +1490,21 @@ var getMigrationDate = () => {
|
|
|
1711
1490
|
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
1712
1491
|
};
|
|
1713
1492
|
|
|
1714
|
-
// src/
|
|
1715
|
-
var
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
pg_get_functiondef(p.oid) as definition
|
|
1719
|
-
FROM pg_proc p
|
|
1720
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1721
|
-
WHERE n.nspname = 'public'
|
|
1722
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
1723
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1724
|
-
`);
|
|
1725
|
-
const aggregateFunctions = await knex2.raw(`
|
|
1726
|
-
SELECT
|
|
1727
|
-
p.proname as name,
|
|
1728
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1729
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
1730
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
1731
|
-
a.agginitval as init_val,
|
|
1732
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type
|
|
1733
|
-
FROM pg_proc p
|
|
1734
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
1735
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1736
|
-
WHERE n.nspname = 'public'
|
|
1737
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1738
|
-
`);
|
|
1739
|
-
const functions = [];
|
|
1740
|
-
for (const row of regularFunctions.rows || []) {
|
|
1741
|
-
if (row.definition) {
|
|
1742
|
-
functions.push(row.definition.trim());
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
1746
|
-
const name2 = row.name || "";
|
|
1747
|
-
const argumentsStr = row.arguments || "";
|
|
1748
|
-
const transFunc = row.trans_func || "";
|
|
1749
|
-
const finalFunc = row.final_func || "";
|
|
1750
|
-
const initVal = row.init_val;
|
|
1751
|
-
const stateType = row.state_type || "";
|
|
1752
|
-
if (!name2 || !transFunc || !stateType) {
|
|
1753
|
-
continue;
|
|
1754
|
-
}
|
|
1755
|
-
let aggregateDef = `CREATE AGGREGATE ${name2}(${argumentsStr}) (
|
|
1756
|
-
`;
|
|
1757
|
-
aggregateDef += ` SFUNC = ${transFunc},
|
|
1758
|
-
`;
|
|
1759
|
-
aggregateDef += ` STYPE = ${stateType}`;
|
|
1760
|
-
if (finalFunc) {
|
|
1761
|
-
aggregateDef += `,
|
|
1762
|
-
FINALFUNC = ${finalFunc}`;
|
|
1763
|
-
}
|
|
1764
|
-
if (initVal !== null && initVal !== void 0) {
|
|
1765
|
-
const initValStr = typeof initVal === "string" ? `'${initVal}'` : String(initVal);
|
|
1766
|
-
aggregateDef += `,
|
|
1767
|
-
INITCOND = ${initValStr}`;
|
|
1768
|
-
}
|
|
1769
|
-
aggregateDef += "\n);";
|
|
1770
|
-
functions.push(aggregateDef);
|
|
1771
|
-
}
|
|
1772
|
-
if (functions.length === 0) {
|
|
1773
|
-
return `export const functions: string[] = [];
|
|
1774
|
-
`;
|
|
1775
|
-
}
|
|
1776
|
-
const functionsArrayString = functions.map((func) => ` ${JSON.stringify(func)}`).join(",\n");
|
|
1777
|
-
return `export const functions: string[] = [
|
|
1778
|
-
${functionsArrayString},
|
|
1779
|
-
];
|
|
1780
|
-
`;
|
|
1781
|
-
};
|
|
1782
|
-
|
|
1783
|
-
// src/migrations/update-functions.ts
|
|
1784
|
-
var normalizeWhitespace = (str) => {
|
|
1785
|
-
return str.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").replace(/\s*;\s*/g, ";").trim();
|
|
1786
|
-
};
|
|
1787
|
-
var normalizeFunctionBody = (body) => {
|
|
1788
|
-
return normalizeWhitespace(body);
|
|
1789
|
-
};
|
|
1790
|
-
var extractFunctionBody = (definition) => {
|
|
1791
|
-
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
1792
|
-
if (dollarQuoteMatch) {
|
|
1793
|
-
return dollarQuoteMatch[2].trim();
|
|
1794
|
-
}
|
|
1795
|
-
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
1796
|
-
if (bodyMatch) {
|
|
1797
|
-
return bodyMatch[1].trim();
|
|
1798
|
-
}
|
|
1799
|
-
return definition;
|
|
1800
|
-
};
|
|
1801
|
-
var getDatabaseFunctions = async (knex2) => {
|
|
1802
|
-
const regularFunctions = await knex2.raw(`
|
|
1803
|
-
SELECT
|
|
1804
|
-
p.proname as name,
|
|
1805
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1806
|
-
pg_get_functiondef(p.oid) as definition,
|
|
1807
|
-
false as is_aggregate
|
|
1808
|
-
FROM pg_proc p
|
|
1809
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1810
|
-
WHERE n.nspname = 'public'
|
|
1811
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
1812
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1813
|
-
`);
|
|
1814
|
-
const aggregateFunctions = await knex2.raw(`
|
|
1815
|
-
SELECT
|
|
1816
|
-
p.proname as name,
|
|
1817
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
1818
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
1819
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
1820
|
-
a.agginitval as init_val,
|
|
1821
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
|
|
1822
|
-
true as is_aggregate
|
|
1823
|
-
FROM pg_proc p
|
|
1824
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
1825
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
1826
|
-
WHERE n.nspname = 'public'
|
|
1827
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
1828
|
-
`);
|
|
1829
|
-
const result = [];
|
|
1830
|
-
for (const row of regularFunctions.rows || []) {
|
|
1831
|
-
const definition = row.definition || "";
|
|
1832
|
-
const name2 = row.name || "";
|
|
1833
|
-
const argumentsStr = row.arguments || "";
|
|
1834
|
-
if (!definition) {
|
|
1835
|
-
continue;
|
|
1836
|
-
}
|
|
1837
|
-
const signature = `${name2}(${argumentsStr})`;
|
|
1838
|
-
const body = normalizeFunctionBody(extractFunctionBody(definition));
|
|
1839
|
-
result.push({
|
|
1840
|
-
name: name2,
|
|
1841
|
-
signature,
|
|
1842
|
-
body,
|
|
1843
|
-
isAggregate: false,
|
|
1844
|
-
definition
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
1848
|
-
const name2 = row.name || "";
|
|
1849
|
-
const argumentsStr = row.arguments || "";
|
|
1850
|
-
const transFunc = row.trans_func || "";
|
|
1851
|
-
const finalFunc = row.final_func || "";
|
|
1852
|
-
const initVal = row.init_val;
|
|
1853
|
-
const stateType = row.state_type || "";
|
|
1854
|
-
const signature = `${name2}(${argumentsStr})`;
|
|
1855
|
-
let aggregateDef = `CREATE AGGREGATE ${name2}(${argumentsStr}) (`;
|
|
1856
|
-
aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
|
|
1857
|
-
if (finalFunc) {
|
|
1858
|
-
aggregateDef += `, FINALFUNC = ${finalFunc}`;
|
|
1859
|
-
}
|
|
1860
|
-
if (initVal !== null && initVal !== void 0) {
|
|
1861
|
-
const initValStr = typeof initVal === "string" ? `'${initVal}'` : String(initVal);
|
|
1862
|
-
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
1863
|
-
}
|
|
1864
|
-
aggregateDef += ");";
|
|
1865
|
-
result.push({
|
|
1866
|
-
name: name2,
|
|
1867
|
-
signature,
|
|
1868
|
-
body: normalizeFunctionBody(aggregateDef),
|
|
1869
|
-
isAggregate: true,
|
|
1870
|
-
definition: aggregateDef
|
|
1871
|
-
});
|
|
1872
|
-
}
|
|
1873
|
-
return result;
|
|
1874
|
-
};
|
|
1875
|
-
var compareFunctions = (defined, db) => {
|
|
1876
|
-
const definedBody = normalizeFunctionBody(defined.body);
|
|
1877
|
-
const dbBody = normalizeFunctionBody(db.body);
|
|
1878
|
-
if (definedBody !== dbBody) {
|
|
1879
|
-
const definedPreview = definedBody.length > 200 ? `${definedBody.substring(0, 200)}...` : definedBody;
|
|
1880
|
-
const dbPreview = dbBody.length > 200 ? `${dbBody.substring(0, 200)}...` : dbBody;
|
|
1881
|
-
return {
|
|
1882
|
-
changed: true,
|
|
1883
|
-
diff: `Definition changed:
|
|
1884
|
-
File: ${definedPreview}
|
|
1885
|
-
DB: ${dbPreview}`
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
return { changed: false };
|
|
1889
|
-
};
|
|
1890
|
-
var updateFunctions = async (knex2, parsedFunctions) => {
|
|
1891
|
-
if (parsedFunctions.length === 0) {
|
|
1892
|
-
return;
|
|
1893
|
-
}
|
|
1894
|
-
const definedFunctions = parsedFunctions;
|
|
1895
|
-
const dbFunctions = await getDatabaseFunctions(knex2);
|
|
1896
|
-
const dbFunctionsBySignature = /* @__PURE__ */ new Map();
|
|
1897
|
-
for (const func of dbFunctions) {
|
|
1898
|
-
dbFunctionsBySignature.set(func.signature, func);
|
|
1899
|
-
}
|
|
1900
|
-
console.info(`Found ${definedFunctions.length} function(s) in file, ${dbFunctions.length} function(s) in database.`);
|
|
1901
|
-
let updatedCount = 0;
|
|
1902
|
-
let skippedCount = 0;
|
|
1903
|
-
for (const definedFunc of definedFunctions) {
|
|
1904
|
-
const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
|
|
1905
|
-
if (!dbFunc) {
|
|
1906
|
-
try {
|
|
1907
|
-
await knex2.raw(definedFunc.fullDefinition);
|
|
1908
|
-
console.info(`\u2713 Created ${definedFunc.isAggregate ? "aggregate" : "function"}: ${definedFunc.signature}`);
|
|
1909
|
-
updatedCount++;
|
|
1910
|
-
} catch (error) {
|
|
1911
|
-
console.error(
|
|
1912
|
-
`\u2717 Failed to create ${definedFunc.isAggregate ? "aggregate" : "function"} ${definedFunc.signature}:`,
|
|
1913
|
-
error.message
|
|
1914
|
-
);
|
|
1915
|
-
throw error;
|
|
1916
|
-
}
|
|
1917
|
-
} else {
|
|
1918
|
-
const comparison = compareFunctions(definedFunc, dbFunc);
|
|
1919
|
-
if (comparison.changed) {
|
|
1920
|
-
console.info(`
|
|
1921
|
-
\u26A0 ${definedFunc.isAggregate ? "Aggregate" : "Function"} ${definedFunc.signature} has changes:`);
|
|
1922
|
-
if (comparison.diff) {
|
|
1923
|
-
console.info(comparison.diff);
|
|
1924
|
-
}
|
|
1925
|
-
try {
|
|
1926
|
-
if (definedFunc.isAggregate) {
|
|
1927
|
-
const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
|
|
1928
|
-
if (dropMatch) {
|
|
1929
|
-
const functionName = dropMatch[2].trim();
|
|
1930
|
-
const argsMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
|
|
1931
|
-
const args2 = argsMatch ? argsMatch[2].trim() : "";
|
|
1932
|
-
await knex2.raw(`DROP AGGREGATE IF EXISTS ${functionName}${args2 ? `(${args2})` : ""}`);
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
await knex2.raw(definedFunc.fullDefinition);
|
|
1936
|
-
console.info(`\u2713 Updated ${definedFunc.isAggregate ? "aggregate" : "function"}: ${definedFunc.signature}
|
|
1937
|
-
`);
|
|
1938
|
-
updatedCount++;
|
|
1939
|
-
} catch (error) {
|
|
1940
|
-
console.error(
|
|
1941
|
-
`\u2717 Failed to update ${definedFunc.isAggregate ? "aggregate" : "function"} ${definedFunc.signature}:`,
|
|
1942
|
-
error.message
|
|
1943
|
-
);
|
|
1944
|
-
throw error;
|
|
1945
|
-
}
|
|
1946
|
-
} else {
|
|
1947
|
-
console.info(
|
|
1948
|
-
`\u25CB Skipped ${definedFunc.isAggregate ? "aggregate" : "function"} (unchanged): ${definedFunc.signature}`
|
|
1949
|
-
);
|
|
1950
|
-
skippedCount++;
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
console.info(`
|
|
1955
|
-
Summary: ${updatedCount} updated, ${skippedCount} skipped`);
|
|
1956
|
-
if (updatedCount > 0) {
|
|
1957
|
-
console.info("Functions updated successfully.");
|
|
1958
|
-
} else {
|
|
1959
|
-
console.info("All functions are up to date.");
|
|
1960
|
-
}
|
|
1961
|
-
};
|
|
1493
|
+
// src/resolvers/utils.ts
|
|
1494
|
+
var import_graphql3 = require("graphql");
|
|
1495
|
+
var import_isEqual = __toESM(require("lodash/isEqual"), 1);
|
|
1496
|
+
var getColumnName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
|
|
1962
1497
|
|
|
1963
1498
|
// src/permissions/generate.ts
|
|
1964
1499
|
var ACTIONS = ["READ", "CREATE", "UPDATE", "DELETE", "RESTORE", "LINK"];
|
|
1965
1500
|
|
|
1501
|
+
// src/resolvers/arguments.ts
|
|
1502
|
+
var import_graphql4 = require("graphql");
|
|
1503
|
+
|
|
1504
|
+
// src/resolvers/resolver.ts
|
|
1505
|
+
var import_cloneDeep2 = __toESM(require("lodash/cloneDeep"), 1);
|
|
1506
|
+
var import_flatMap = __toESM(require("lodash/flatMap"), 1);
|
|
1507
|
+
|
|
1966
1508
|
// src/schema/generate.ts
|
|
1967
1509
|
var import_graphql6 = require("graphql");
|
|
1968
1510
|
|
|
@@ -2540,14 +2082,6 @@ var DEFAULTS = {
|
|
|
2540
2082
|
ensureFileExists(path, EMPTY_MODELS);
|
|
2541
2083
|
}
|
|
2542
2084
|
},
|
|
2543
|
-
functionsPath: {
|
|
2544
|
-
question: "What is the PostgreSQL functions file path?",
|
|
2545
|
-
defaultValue: "src/config/functions.ts",
|
|
2546
|
-
init: (path) => {
|
|
2547
|
-
ensureFileExists(path, `export const functions: string[] = [];
|
|
2548
|
-
`);
|
|
2549
|
-
}
|
|
2550
|
-
},
|
|
2551
2085
|
generatedFolderPath: {
|
|
2552
2086
|
question: "What is the path for generated stuff?",
|
|
2553
2087
|
defaultValue: "src/generated",
|
|
@@ -2727,8 +2261,7 @@ var generateGraphqlClientTypes = async () => {
|
|
|
2727
2261
|
});
|
|
2728
2262
|
};
|
|
2729
2263
|
|
|
2730
|
-
// src/bin/gqm/parse-
|
|
2731
|
-
var import_fs2 = require("fs");
|
|
2264
|
+
// src/bin/gqm/parse-knexfile.ts
|
|
2732
2265
|
var import_ts_morph4 = require("ts-morph");
|
|
2733
2266
|
|
|
2734
2267
|
// src/bin/gqm/static-eval.ts
|
|
@@ -2821,6 +2354,72 @@ var VISITOR = {
|
|
|
2821
2354
|
}),
|
|
2822
2355
|
[import_ts_morph2.SyntaxKind.SpreadElement]: (node, context) => staticEval(node.getExpression(), context),
|
|
2823
2356
|
[import_ts_morph2.SyntaxKind.SpreadAssignment]: (node, context) => staticEval(node.getExpression(), context),
|
|
2357
|
+
[import_ts_morph2.SyntaxKind.ImportSpecifier]: (node, context) => {
|
|
2358
|
+
const nameNode = node.getNameNode();
|
|
2359
|
+
const name2 = nameNode.getText();
|
|
2360
|
+
if (name2 in KNOWN_IDENTIFIERS) {
|
|
2361
|
+
return KNOWN_IDENTIFIERS[name2];
|
|
2362
|
+
}
|
|
2363
|
+
if (nameNode instanceof import_ts_morph2.StringLiteral) {
|
|
2364
|
+
throw new Error(`Cannot handle computed import specifier: ${name2}. Only static imports are supported.`);
|
|
2365
|
+
}
|
|
2366
|
+
const definitions = nameNode.getDefinitionNodes();
|
|
2367
|
+
let externalDefinition = definitions.find((d) => d.compilerNode !== node.compilerNode);
|
|
2368
|
+
if (!externalDefinition) {
|
|
2369
|
+
const importDeclaration = node.getImportDeclaration();
|
|
2370
|
+
let sourceFile = importDeclaration.getModuleSpecifierSourceFile();
|
|
2371
|
+
if (!sourceFile) {
|
|
2372
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
|
|
2373
|
+
const project = node.getProject();
|
|
2374
|
+
if (moduleSpecifier.startsWith("@/")) {
|
|
2375
|
+
const suffix = moduleSpecifier.substring(2);
|
|
2376
|
+
sourceFile = project.getSourceFiles().find((sf) => {
|
|
2377
|
+
const filePath = sf.getFilePath();
|
|
2378
|
+
return filePath.endsWith(`/${suffix}.ts`) || filePath.endsWith(`/${suffix}.tsx`) || filePath.endsWith(`/${suffix}/index.ts`) || filePath.endsWith(`/${suffix}/index.tsx`);
|
|
2379
|
+
});
|
|
2380
|
+
if (!sourceFile) {
|
|
2381
|
+
const candidates = [
|
|
2382
|
+
`src/${suffix}.ts`,
|
|
2383
|
+
`src/${suffix}.tsx`,
|
|
2384
|
+
`src/${suffix}/index.ts`,
|
|
2385
|
+
`src/${suffix}/index.tsx`
|
|
2386
|
+
];
|
|
2387
|
+
for (const candidate of candidates) {
|
|
2388
|
+
try {
|
|
2389
|
+
const added = project.addSourceFileAtPathIfExists(candidate);
|
|
2390
|
+
if (added) {
|
|
2391
|
+
sourceFile = added;
|
|
2392
|
+
break;
|
|
2393
|
+
}
|
|
2394
|
+
} catch {
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
if (sourceFile) {
|
|
2401
|
+
const localName = node.getName();
|
|
2402
|
+
const propertyName = node.compilerNode.propertyName?.getText();
|
|
2403
|
+
const exportedName = propertyName ?? localName;
|
|
2404
|
+
const exportedDeclarations = sourceFile.getExportedDeclarations();
|
|
2405
|
+
const declarations = exportedDeclarations.get(exportedName);
|
|
2406
|
+
const declaration = declarations?.[0];
|
|
2407
|
+
if (declaration) {
|
|
2408
|
+
externalDefinition = declaration;
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
if (externalDefinition && externalDefinition.getKind() === import_ts_morph2.SyntaxKind.ImportSpecifier) {
|
|
2413
|
+
return staticEval(externalDefinition, context);
|
|
2414
|
+
}
|
|
2415
|
+
if (!externalDefinition) {
|
|
2416
|
+
const importDeclaration = node.getImportDeclaration();
|
|
2417
|
+
throw new Error(
|
|
2418
|
+
`No definition node found for import specifier '${name2}' imported from '${importDeclaration.getModuleSpecifierValue()}'.`
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
return staticEval(externalDefinition, context);
|
|
2422
|
+
},
|
|
2824
2423
|
[import_ts_morph2.SyntaxKind.Identifier]: (node, context) => {
|
|
2825
2424
|
const identifierName = node.getText();
|
|
2826
2425
|
if (identifierName in KNOWN_IDENTIFIERS) {
|
|
@@ -3047,114 +2646,11 @@ var findDeclaration = (syntaxList, name2) => {
|
|
|
3047
2646
|
}
|
|
3048
2647
|
};
|
|
3049
2648
|
|
|
3050
|
-
// src/bin/gqm/parse-functions.ts
|
|
3051
|
-
var normalizeWhitespace2 = (str) => {
|
|
3052
|
-
return str.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").replace(/\s*;\s*/g, ";").trim();
|
|
3053
|
-
};
|
|
3054
|
-
var normalizeFunctionBody2 = (body) => {
|
|
3055
|
-
return normalizeWhitespace2(body);
|
|
3056
|
-
};
|
|
3057
|
-
var normalizeAggregateDefinition = (definition) => {
|
|
3058
|
-
let normalized = normalizeWhitespace2(definition);
|
|
3059
|
-
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
3060
|
-
if (initCondMatch) {
|
|
3061
|
-
const initCondValue = initCondMatch[1].trim();
|
|
3062
|
-
const unquoted = initCondValue.replace(/^['"]|['"]$/g, "");
|
|
3063
|
-
if (/^\d+$/.test(unquoted)) {
|
|
3064
|
-
normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
return normalized;
|
|
3068
|
-
};
|
|
3069
|
-
var extractFunctionSignature = (definition, isAggregate) => {
|
|
3070
|
-
if (isAggregate) {
|
|
3071
|
-
const createMatch2 = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
|
|
3072
|
-
if (!createMatch2) {
|
|
3073
|
-
return null;
|
|
3074
|
-
}
|
|
3075
|
-
const functionNamePart2 = createMatch2[2].trim().replace(/^[^.]+\./, "");
|
|
3076
|
-
const argsMatch = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
|
|
3077
|
-
const args3 = argsMatch ? argsMatch[2].trim() : "";
|
|
3078
|
-
return `${functionNamePart2}(${args3})`;
|
|
3079
|
-
}
|
|
3080
|
-
const createMatch = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+([^(]+)\(/i);
|
|
3081
|
-
if (!createMatch) {
|
|
3082
|
-
return null;
|
|
3083
|
-
}
|
|
3084
|
-
const functionNamePart = createMatch[2].trim().replace(/^[^.]+\./, "");
|
|
3085
|
-
const fullArgsMatch = definition.match(
|
|
3086
|
-
/CREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+[^(]+\(([\s\S]*?)\)\s*(RETURNS|LANGUAGE|AS|STRICT|IMMUTABLE|STABLE|VOLATILE|SECURITY)/i
|
|
3087
|
-
);
|
|
3088
|
-
if (!fullArgsMatch) {
|
|
3089
|
-
return null;
|
|
3090
|
-
}
|
|
3091
|
-
const argsSection = fullArgsMatch[2].trim();
|
|
3092
|
-
const args2 = argsSection.split(/\s*,\s*/).map((arg) => {
|
|
3093
|
-
return arg.trim().replace(/\s+/g, " ");
|
|
3094
|
-
}).join(", ");
|
|
3095
|
-
return `${functionNamePart}(${args2})`;
|
|
3096
|
-
};
|
|
3097
|
-
var extractFunctionBody2 = (definition) => {
|
|
3098
|
-
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
3099
|
-
if (dollarQuoteMatch) {
|
|
3100
|
-
return dollarQuoteMatch[2].trim();
|
|
3101
|
-
}
|
|
3102
|
-
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
3103
|
-
if (bodyMatch) {
|
|
3104
|
-
return bodyMatch[1].trim();
|
|
3105
|
-
}
|
|
3106
|
-
return definition;
|
|
3107
|
-
};
|
|
3108
|
-
var parseFunctionsFile = (filePath) => {
|
|
3109
|
-
if (!(0, import_fs2.existsSync)(filePath)) {
|
|
3110
|
-
return [];
|
|
3111
|
-
}
|
|
3112
|
-
const project = new import_ts_morph4.Project({
|
|
3113
|
-
manipulationSettings: {
|
|
3114
|
-
indentationText: import_ts_morph4.IndentationText.TwoSpaces
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
3118
|
-
try {
|
|
3119
|
-
const functionsDeclaration = findDeclarationInFile(sourceFile, "functions");
|
|
3120
|
-
const functionsArray = staticEval(functionsDeclaration, {});
|
|
3121
|
-
if (!Array.isArray(functionsArray)) {
|
|
3122
|
-
return [];
|
|
3123
|
-
}
|
|
3124
|
-
const parsedFunctions = [];
|
|
3125
|
-
for (const definition of functionsArray) {
|
|
3126
|
-
if (!definition || typeof definition !== "string") {
|
|
3127
|
-
continue;
|
|
3128
|
-
}
|
|
3129
|
-
const trimmedDefinition = definition.trim();
|
|
3130
|
-
const isAggregate = /CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE/i.test(trimmedDefinition);
|
|
3131
|
-
const signature = extractFunctionSignature(trimmedDefinition, isAggregate);
|
|
3132
|
-
if (!signature) {
|
|
3133
|
-
continue;
|
|
3134
|
-
}
|
|
3135
|
-
const nameMatch = signature.match(/^([^(]+)\(/);
|
|
3136
|
-
const name2 = nameMatch ? nameMatch[1].trim().split(".").pop() || "" : "";
|
|
3137
|
-
const body = isAggregate ? trimmedDefinition : extractFunctionBody2(trimmedDefinition);
|
|
3138
|
-
parsedFunctions.push({
|
|
3139
|
-
name: name2,
|
|
3140
|
-
signature,
|
|
3141
|
-
body: isAggregate ? normalizeAggregateDefinition(body) : normalizeFunctionBody2(body),
|
|
3142
|
-
fullDefinition: trimmedDefinition,
|
|
3143
|
-
isAggregate
|
|
3144
|
-
});
|
|
3145
|
-
}
|
|
3146
|
-
return parsedFunctions;
|
|
3147
|
-
} catch (error) {
|
|
3148
|
-
return [];
|
|
3149
|
-
}
|
|
3150
|
-
};
|
|
3151
|
-
|
|
3152
2649
|
// src/bin/gqm/parse-knexfile.ts
|
|
3153
|
-
var import_ts_morph5 = require("ts-morph");
|
|
3154
2650
|
var parseKnexfile = async () => {
|
|
3155
|
-
const project = new
|
|
2651
|
+
const project = new import_ts_morph4.Project({
|
|
3156
2652
|
manipulationSettings: {
|
|
3157
|
-
indentationText:
|
|
2653
|
+
indentationText: import_ts_morph4.IndentationText.TwoSpaces
|
|
3158
2654
|
}
|
|
3159
2655
|
});
|
|
3160
2656
|
const knexfilePath = await getSetting("knexfilePath");
|
|
@@ -3166,11 +2662,11 @@ var parseKnexfile = async () => {
|
|
|
3166
2662
|
};
|
|
3167
2663
|
|
|
3168
2664
|
// src/bin/gqm/parse-models.ts
|
|
3169
|
-
var
|
|
2665
|
+
var import_ts_morph5 = require("ts-morph");
|
|
3170
2666
|
var parseModels = async () => {
|
|
3171
|
-
const project = new
|
|
2667
|
+
const project = new import_ts_morph5.Project({
|
|
3172
2668
|
manipulationSettings: {
|
|
3173
|
-
indentationText:
|
|
2669
|
+
indentationText: import_ts_morph5.IndentationText.TwoSpaces
|
|
3174
2670
|
}
|
|
3175
2671
|
});
|
|
3176
2672
|
const modelsPath = await getSetting("modelsPath");
|
|
@@ -3183,11 +2679,11 @@ var parseModels = async () => {
|
|
|
3183
2679
|
};
|
|
3184
2680
|
|
|
3185
2681
|
// src/bin/gqm/permissions.ts
|
|
3186
|
-
var
|
|
2682
|
+
var import_ts_morph6 = require("ts-morph");
|
|
3187
2683
|
var generatePermissionTypes = (models) => {
|
|
3188
|
-
const project = new
|
|
2684
|
+
const project = new import_ts_morph6.Project({
|
|
3189
2685
|
manipulationSettings: {
|
|
3190
|
-
indentationText:
|
|
2686
|
+
indentationText: import_ts_morph6.IndentationText.TwoSpaces
|
|
3191
2687
|
}
|
|
3192
2688
|
});
|
|
3193
2689
|
const sourceFile = project.createSourceFile("permissions.ts", "", {
|
|
@@ -3290,9 +2786,7 @@ import_commander.program.command("generate-migration [<name>] [<date>]").descrip
|
|
|
3290
2786
|
const db = (0, import_knex.default)(knexfile);
|
|
3291
2787
|
try {
|
|
3292
2788
|
const models = await parseModels();
|
|
3293
|
-
const
|
|
3294
|
-
const parsedFunctions = parseFunctionsFile(functionsPath);
|
|
3295
|
-
const migrations = await new MigrationGenerator(db, models, parsedFunctions).generate();
|
|
2789
|
+
const migrations = await new MigrationGenerator(db, models).generate();
|
|
3296
2790
|
writeToFile(`migrations/${date || getMigrationDate()}_${name2}.ts`, migrations);
|
|
3297
2791
|
} finally {
|
|
3298
2792
|
await db.destroy();
|
|
@@ -3303,9 +2797,7 @@ import_commander.program.command("check-needs-migration").description("Check if
|
|
|
3303
2797
|
const db = (0, import_knex.default)(knexfile);
|
|
3304
2798
|
try {
|
|
3305
2799
|
const models = await parseModels();
|
|
3306
|
-
const
|
|
3307
|
-
const parsedFunctions = parseFunctionsFile(functionsPath);
|
|
3308
|
-
const mg = new MigrationGenerator(db, models, parsedFunctions);
|
|
2800
|
+
const mg = new MigrationGenerator(db, models);
|
|
3309
2801
|
await mg.generate();
|
|
3310
2802
|
if (mg.needsMigration) {
|
|
3311
2803
|
console.error("Migration is needed.");
|
|
@@ -3315,31 +2807,8 @@ import_commander.program.command("check-needs-migration").description("Check if
|
|
|
3315
2807
|
await db.destroy();
|
|
3316
2808
|
}
|
|
3317
2809
|
});
|
|
3318
|
-
import_commander.program.command("
|
|
3319
|
-
|
|
3320
|
-
const db = (0, import_knex.default)(knexfile);
|
|
3321
|
-
try {
|
|
3322
|
-
const functionsPath = await getSetting("functionsPath");
|
|
3323
|
-
const functions = await generateFunctionsFromDatabase(db);
|
|
3324
|
-
writeToFile(functionsPath, functions);
|
|
3325
|
-
} finally {
|
|
3326
|
-
await db.destroy();
|
|
3327
|
-
}
|
|
3328
|
-
});
|
|
3329
|
-
import_commander.program.command("update-functions").description("Update database functions from functions.ts file").action(async () => {
|
|
3330
|
-
const knexfile = await parseKnexfile();
|
|
3331
|
-
const db = (0, import_knex.default)(knexfile);
|
|
3332
|
-
try {
|
|
3333
|
-
const functionsPath = await getSetting("functionsPath");
|
|
3334
|
-
const parsedFunctions = parseFunctionsFile(functionsPath);
|
|
3335
|
-
await updateFunctions(db, parsedFunctions);
|
|
3336
|
-
} finally {
|
|
3337
|
-
await db.destroy();
|
|
3338
|
-
}
|
|
3339
|
-
});
|
|
3340
|
-
import_commander.program.command("*").description("Invalid command").action((command) => {
|
|
3341
|
-
console.error(`Invalid command: ${command}
|
|
3342
|
-
See --help for a list of available commands.`);
|
|
2810
|
+
import_commander.program.command("*", { noHelp: true }).description("Invalid command").action(() => {
|
|
2811
|
+
console.error("Invalid command: %s\nSee --help for a list of available commands.", import_commander.program.args.join(" "));
|
|
3343
2812
|
process.exit(1);
|
|
3344
2813
|
});
|
|
3345
2814
|
import_commander.program.parse(process.argv);
|