@smartive/graphql-magic 23.5.0-next.1 → 23.5.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.
- package/CHANGELOG.md +2 -17
- package/dist/bin/gqm.cjs +204 -292
- package/dist/cjs/index.cjs +210 -290
- package/dist/esm/migrations/generate.d.ts +0 -4
- package/dist/esm/migrations/generate.js +6 -124
- package/dist/esm/migrations/generate.js.map +1 -1
- package/dist/esm/migrations/update-functions.d.ts +11 -0
- package/dist/esm/migrations/update-functions.js +29 -5
- package/dist/esm/migrations/update-functions.js.map +1 -1
- package/package.json +1 -1
- package/src/migrations/generate.ts +11 -153
- package/src/migrations/update-functions.ts +32 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
## [23.5.0
|
|
1
|
+
## [23.5.0](https://github.com/smartive/graphql-magic/compare/v23.4.1...v23.5.0) (2026-02-09)
|
|
2
2
|
|
|
3
3
|
### Features
|
|
4
4
|
|
|
5
|
-
*
|
|
6
|
-
* Use functions.ts instead of functions.sql ([58e0653](https://github.com/smartive/graphql-magic/commit/58e065347b2f3f1c80691911b5191af38586c1e2))
|
|
7
|
-
|
|
8
|
-
### Bug Fixes
|
|
9
|
-
|
|
10
|
-
* Functions code order ([cf7f7a4](https://github.com/smartive/graphql-magic/commit/cf7f7a4421e8297af866c3ecc04b5e672862e6ab))
|
|
11
|
-
* Generated expression handling ([e325eb2](https://github.com/smartive/graphql-magic/commit/e325eb232b7bbbbe7d65f9fae5221f4a764a8600))
|
|
12
|
-
* improve handling of missing revision fields in migration updates ([4296662](https://github.com/smartive/graphql-magic/commit/429666247224ef782280fd6144cb749468b934cd))
|
|
13
|
-
* Index ([42d109f](https://github.com/smartive/graphql-magic/commit/42d109fc6dbbf8fc7937f076403a69885779d8f6))
|
|
14
|
-
* Linting ([2e997e4](https://github.com/smartive/graphql-magic/commit/2e997e464371a9d8cf05ceb67a6cd29a9452f615))
|
|
15
|
-
* Omit more stuff ([df2ae04](https://github.com/smartive/graphql-magic/commit/df2ae044557166e876b7646d6ebe67a78df2722e))
|
|
16
|
-
* Permissions ([c198115](https://github.com/smartive/graphql-magic/commit/c19811552ac3de05d4c1069116620067ec0a5540))
|
|
17
|
-
* Temporarily disable tags ([52962c1](https://github.com/smartive/graphql-magic/commit/52962c1a792c1ae010ae542c23d50dd4f8486dde))
|
|
18
|
-
* Trigger release ([2cd67b9](https://github.com/smartive/graphql-magic/commit/2cd67b91d33d101793c04eb87ad9c226bc0fdd0f))
|
|
19
|
-
* Trigger release ([d5ac376](https://github.com/smartive/graphql-magic/commit/d5ac376533460921a638c71f4c9a69c83a057897))
|
|
20
|
-
* Update functions ([3717f8a](https://github.com/smartive/graphql-magic/commit/3717f8aba9d908fbde8d2f49e7c21f87fe6a8d12))
|
|
5
|
+
* Functions migration generator ([0525bb5](https://github.com/smartive/graphql-magic/commit/0525bb5d182bdd258fd6408be32bfb9ce5146faa))
|
package/dist/bin/gqm.cjs
CHANGED
|
@@ -763,6 +763,204 @@ var getColumnName = (field) => field.kind === "relation" ? field.foreignKey || `
|
|
|
763
763
|
var import_cloneDeep2 = __toESM(require("lodash/cloneDeep"), 1);
|
|
764
764
|
var import_flatMap = __toESM(require("lodash/flatMap"), 1);
|
|
765
765
|
|
|
766
|
+
// src/migrations/update-functions.ts
|
|
767
|
+
var normalizeWhitespace = (str) => {
|
|
768
|
+
return str.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").replace(/\s*;\s*/g, ";").trim();
|
|
769
|
+
};
|
|
770
|
+
var normalizeFunctionBody = (body) => {
|
|
771
|
+
return normalizeWhitespace(body);
|
|
772
|
+
};
|
|
773
|
+
var normalizeAggregateDefinition = (definition) => {
|
|
774
|
+
let normalized = definition.replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*\)\s*/g, ")").replace(/\s*,\s*/g, ",").trim();
|
|
775
|
+
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
776
|
+
if (initCondMatch) {
|
|
777
|
+
const initCondValue = initCondMatch[1].trim();
|
|
778
|
+
const unquoted = initCondValue.replace(/^['"]|['"]$/g, "");
|
|
779
|
+
if (/^\d+$/.test(unquoted)) {
|
|
780
|
+
normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return normalized;
|
|
784
|
+
};
|
|
785
|
+
var extractFunctionBody = (definition) => {
|
|
786
|
+
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
787
|
+
if (dollarQuoteMatch) {
|
|
788
|
+
return dollarQuoteMatch[2].trim();
|
|
789
|
+
}
|
|
790
|
+
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
791
|
+
if (bodyMatch) {
|
|
792
|
+
return bodyMatch[1].trim();
|
|
793
|
+
}
|
|
794
|
+
return definition;
|
|
795
|
+
};
|
|
796
|
+
var getDatabaseFunctions = async (knex2) => {
|
|
797
|
+
const regularFunctions = await knex2.raw(`
|
|
798
|
+
SELECT
|
|
799
|
+
p.proname as name,
|
|
800
|
+
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
801
|
+
pg_get_functiondef(p.oid) as definition,
|
|
802
|
+
false as is_aggregate
|
|
803
|
+
FROM pg_proc p
|
|
804
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
805
|
+
WHERE n.nspname = 'public'
|
|
806
|
+
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
807
|
+
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
808
|
+
`);
|
|
809
|
+
const aggregateFunctions = await knex2.raw(`
|
|
810
|
+
SELECT
|
|
811
|
+
p.proname as name,
|
|
812
|
+
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
813
|
+
a.aggtransfn::regproc::text as trans_func,
|
|
814
|
+
a.aggfinalfn::regproc::text as final_func,
|
|
815
|
+
a.agginitval as init_val,
|
|
816
|
+
pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
|
|
817
|
+
true as is_aggregate
|
|
818
|
+
FROM pg_proc p
|
|
819
|
+
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
820
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
821
|
+
WHERE n.nspname = 'public'
|
|
822
|
+
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
823
|
+
`);
|
|
824
|
+
const result = [];
|
|
825
|
+
for (const row of regularFunctions.rows || []) {
|
|
826
|
+
const definition = row.definition || "";
|
|
827
|
+
const name2 = row.name || "";
|
|
828
|
+
const argumentsStr = row.arguments || "";
|
|
829
|
+
if (!definition) {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
const signature = `${name2}(${argumentsStr})`;
|
|
833
|
+
const body = normalizeFunctionBody(extractFunctionBody(definition));
|
|
834
|
+
result.push({
|
|
835
|
+
name: name2,
|
|
836
|
+
signature,
|
|
837
|
+
body,
|
|
838
|
+
isAggregate: false,
|
|
839
|
+
definition
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
for (const row of aggregateFunctions.rows || []) {
|
|
843
|
+
const name2 = row.name || "";
|
|
844
|
+
const argumentsStr = row.arguments || "";
|
|
845
|
+
const transFunc = row.trans_func || "";
|
|
846
|
+
const finalFunc = row.final_func || "";
|
|
847
|
+
const initVal = row.init_val;
|
|
848
|
+
const stateType = row.state_type || "";
|
|
849
|
+
const signature = `${name2}(${argumentsStr})`;
|
|
850
|
+
let aggregateDef = `CREATE AGGREGATE ${name2}(${argumentsStr}) (`;
|
|
851
|
+
aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
|
|
852
|
+
if (finalFunc) {
|
|
853
|
+
aggregateDef += `, FINALFUNC = ${finalFunc}`;
|
|
854
|
+
}
|
|
855
|
+
if (initVal !== null && initVal !== void 0) {
|
|
856
|
+
let initValStr;
|
|
857
|
+
if (typeof initVal === "string") {
|
|
858
|
+
initValStr = `'${initVal}'`;
|
|
859
|
+
} else {
|
|
860
|
+
const numStr = String(initVal);
|
|
861
|
+
initValStr = /^\d+$/.test(numStr) ? `'${numStr}'` : numStr;
|
|
862
|
+
}
|
|
863
|
+
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
864
|
+
}
|
|
865
|
+
aggregateDef += ");";
|
|
866
|
+
result.push({
|
|
867
|
+
name: name2,
|
|
868
|
+
signature,
|
|
869
|
+
body: normalizeAggregateDefinition(aggregateDef),
|
|
870
|
+
isAggregate: true,
|
|
871
|
+
definition: aggregateDef
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
return result;
|
|
875
|
+
};
|
|
876
|
+
var compareFunctions = (defined, db) => {
|
|
877
|
+
const definedBody = normalizeFunctionBody(defined.body);
|
|
878
|
+
const dbBody = normalizeFunctionBody(db.body);
|
|
879
|
+
if (definedBody !== dbBody) {
|
|
880
|
+
const definedPreview = definedBody.length > 200 ? `${definedBody.substring(0, 200)}...` : definedBody;
|
|
881
|
+
const dbPreview = dbBody.length > 200 ? `${dbBody.substring(0, 200)}...` : dbBody;
|
|
882
|
+
return {
|
|
883
|
+
changed: true,
|
|
884
|
+
diff: `Definition changed:
|
|
885
|
+
File: ${definedPreview}
|
|
886
|
+
DB: ${dbPreview}`
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
return { changed: false };
|
|
890
|
+
};
|
|
891
|
+
var updateFunctions = async (knex2, parsedFunctions) => {
|
|
892
|
+
if (parsedFunctions.length === 0) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const definedFunctions = parsedFunctions;
|
|
896
|
+
const dbFunctions = await getDatabaseFunctions(knex2);
|
|
897
|
+
const dbFunctionsBySignature = /* @__PURE__ */ new Map();
|
|
898
|
+
for (const func of dbFunctions) {
|
|
899
|
+
dbFunctionsBySignature.set(func.signature, func);
|
|
900
|
+
}
|
|
901
|
+
console.info(`Found ${definedFunctions.length} function(s) in file, ${dbFunctions.length} function(s) in database.`);
|
|
902
|
+
let updatedCount = 0;
|
|
903
|
+
let skippedCount = 0;
|
|
904
|
+
for (const definedFunc of definedFunctions) {
|
|
905
|
+
const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
|
|
906
|
+
if (!dbFunc) {
|
|
907
|
+
try {
|
|
908
|
+
await knex2.raw(definedFunc.fullDefinition);
|
|
909
|
+
console.info(`\u2713 Created ${definedFunc.isAggregate ? "aggregate" : "function"}: ${definedFunc.signature}`);
|
|
910
|
+
updatedCount++;
|
|
911
|
+
} catch (error) {
|
|
912
|
+
console.error(
|
|
913
|
+
`\u2717 Failed to create ${definedFunc.isAggregate ? "aggregate" : "function"} ${definedFunc.signature}:`,
|
|
914
|
+
error.message
|
|
915
|
+
);
|
|
916
|
+
throw error;
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
const comparison = compareFunctions(definedFunc, dbFunc);
|
|
920
|
+
if (comparison.changed) {
|
|
921
|
+
console.info(`
|
|
922
|
+
\u26A0 ${definedFunc.isAggregate ? "Aggregate" : "Function"} ${definedFunc.signature} has changes:`);
|
|
923
|
+
if (comparison.diff) {
|
|
924
|
+
console.info(comparison.diff);
|
|
925
|
+
}
|
|
926
|
+
try {
|
|
927
|
+
if (definedFunc.isAggregate) {
|
|
928
|
+
const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
|
|
929
|
+
if (dropMatch) {
|
|
930
|
+
const functionName = dropMatch[2].trim();
|
|
931
|
+
const argsMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
|
|
932
|
+
const args2 = argsMatch ? argsMatch[2].trim() : "";
|
|
933
|
+
await knex2.raw(`DROP AGGREGATE IF EXISTS ${functionName}${args2 ? `(${args2})` : ""}`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
await knex2.raw(definedFunc.fullDefinition);
|
|
937
|
+
console.info(`\u2713 Updated ${definedFunc.isAggregate ? "aggregate" : "function"}: ${definedFunc.signature}
|
|
938
|
+
`);
|
|
939
|
+
updatedCount++;
|
|
940
|
+
} catch (error) {
|
|
941
|
+
console.error(
|
|
942
|
+
`\u2717 Failed to update ${definedFunc.isAggregate ? "aggregate" : "function"} ${definedFunc.signature}:`,
|
|
943
|
+
error.message
|
|
944
|
+
);
|
|
945
|
+
throw error;
|
|
946
|
+
}
|
|
947
|
+
} else {
|
|
948
|
+
console.info(
|
|
949
|
+
`\u25CB Skipped ${definedFunc.isAggregate ? "aggregate" : "function"} (unchanged): ${definedFunc.signature}`
|
|
950
|
+
);
|
|
951
|
+
skippedCount++;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
console.info(`
|
|
956
|
+
Summary: ${updatedCount} updated, ${skippedCount} skipped`);
|
|
957
|
+
if (updatedCount > 0) {
|
|
958
|
+
console.info("Functions updated successfully.");
|
|
959
|
+
} else {
|
|
960
|
+
console.info("All functions are up to date.");
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
|
|
766
964
|
// src/migrations/generate.ts
|
|
767
965
|
var MigrationGenerator = class {
|
|
768
966
|
constructor(knex2, models, parsedFunctions) {
|
|
@@ -1327,7 +1525,7 @@ var MigrationGenerator = class {
|
|
|
1327
1525
|
this.writer.write(`, ALTER COLUMN "${name2}" SET EXPRESSION AS (${field.generateAs.expression})`);
|
|
1328
1526
|
} else {
|
|
1329
1527
|
this.writer.write(
|
|
1330
|
-
|
|
1528
|
+
`ADD COLUMN "${name2}" ${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs.expression}) STORED`
|
|
1331
1529
|
);
|
|
1332
1530
|
}
|
|
1333
1531
|
return;
|
|
@@ -1523,118 +1721,12 @@ var MigrationGenerator = class {
|
|
|
1523
1721
|
}
|
|
1524
1722
|
return false;
|
|
1525
1723
|
}
|
|
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
1724
|
async handleFunctions(up, down) {
|
|
1633
1725
|
if (!this.parsedFunctions || this.parsedFunctions.length === 0) {
|
|
1634
1726
|
return;
|
|
1635
1727
|
}
|
|
1636
1728
|
const definedFunctions = this.parsedFunctions;
|
|
1637
|
-
const dbFunctions = await this.
|
|
1729
|
+
const dbFunctions = await getDatabaseFunctions(this.knex);
|
|
1638
1730
|
const dbFunctionsBySignature = /* @__PURE__ */ new Map();
|
|
1639
1731
|
for (const func of dbFunctions) {
|
|
1640
1732
|
dbFunctionsBySignature.set(func.signature, func);
|
|
@@ -1664,8 +1756,8 @@ var MigrationGenerator = class {
|
|
|
1664
1756
|
}
|
|
1665
1757
|
});
|
|
1666
1758
|
} else {
|
|
1667
|
-
const dbBody = dbFunc.isAggregate ?
|
|
1668
|
-
const definedBody = definedFunc.isAggregate ?
|
|
1759
|
+
const dbBody = dbFunc.isAggregate ? normalizeAggregateDefinition(dbFunc.body) : normalizeFunctionBody(dbFunc.body);
|
|
1760
|
+
const definedBody = definedFunc.isAggregate ? normalizeAggregateDefinition(definedFunc.body) : normalizeFunctionBody(definedFunc.body);
|
|
1669
1761
|
if (dbBody !== definedBody) {
|
|
1670
1762
|
const oldDefinition = dbFunc.definition || dbFunc.body;
|
|
1671
1763
|
up.push(() => {
|
|
@@ -1780,186 +1872,6 @@ ${functionsArrayString},
|
|
|
1780
1872
|
`;
|
|
1781
1873
|
};
|
|
1782
1874
|
|
|
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
|
-
};
|
|
1962
|
-
|
|
1963
1875
|
// src/permissions/generate.ts
|
|
1964
1876
|
var ACTIONS = ["READ", "CREATE", "UPDATE", "DELETE", "RESTORE", "LINK"];
|
|
1965
1877
|
|
|
@@ -3120,7 +3032,7 @@ var normalizeWhitespace2 = (str) => {
|
|
|
3120
3032
|
var normalizeFunctionBody2 = (body) => {
|
|
3121
3033
|
return normalizeWhitespace2(body);
|
|
3122
3034
|
};
|
|
3123
|
-
var
|
|
3035
|
+
var normalizeAggregateDefinition2 = (definition) => {
|
|
3124
3036
|
let normalized = normalizeWhitespace2(definition);
|
|
3125
3037
|
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
3126
3038
|
if (initCondMatch) {
|
|
@@ -3204,7 +3116,7 @@ var parseFunctionsFile = (filePath) => {
|
|
|
3204
3116
|
parsedFunctions.push({
|
|
3205
3117
|
name: name2,
|
|
3206
3118
|
signature,
|
|
3207
|
-
body: isAggregate ?
|
|
3119
|
+
body: isAggregate ? normalizeAggregateDefinition2(body) : normalizeFunctionBody2(body),
|
|
3208
3120
|
fullDefinition: trimmedDefinition,
|
|
3209
3121
|
isAggregate
|
|
3210
3122
|
});
|