@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
|
@@ -20,17 +20,15 @@ import {
|
|
|
20
20
|
import { getColumnName } from '../resolvers';
|
|
21
21
|
import { Value } from '../values';
|
|
22
22
|
import { ParsedFunction } from './types';
|
|
23
|
+
import {
|
|
24
|
+
DatabaseFunction,
|
|
25
|
+
getDatabaseFunctions,
|
|
26
|
+
normalizeAggregateDefinition,
|
|
27
|
+
normalizeFunctionBody,
|
|
28
|
+
} from './update-functions';
|
|
23
29
|
|
|
24
30
|
type Callbacks = (() => void)[];
|
|
25
31
|
|
|
26
|
-
type DatabaseFunction = {
|
|
27
|
-
name: string;
|
|
28
|
-
signature: string;
|
|
29
|
-
body: string;
|
|
30
|
-
isAggregate: boolean;
|
|
31
|
-
definition?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
32
|
export class MigrationGenerator {
|
|
35
33
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
36
34
|
private writer: CodeBlockWriter = new CodeBlockWriter['default']({
|
|
@@ -762,7 +760,7 @@ export class MigrationGenerator {
|
|
|
762
760
|
this.writer.write(`, ALTER COLUMN "${name}" SET EXPRESSION AS (${field.generateAs.expression})`);
|
|
763
761
|
} else {
|
|
764
762
|
this.writer.write(
|
|
765
|
-
|
|
763
|
+
`ADD COLUMN "${name}" ${type}${nonNull() ? ' not null' : ''} GENERATED ALWAYS AS (${field.generateAs.expression}) STORED`,
|
|
766
764
|
);
|
|
767
765
|
}
|
|
768
766
|
|
|
@@ -981,144 +979,6 @@ export class MigrationGenerator {
|
|
|
981
979
|
return false;
|
|
982
980
|
}
|
|
983
981
|
|
|
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
982
|
private async handleFunctions(up: Callbacks, down: Callbacks) {
|
|
1123
983
|
if (!this.parsedFunctions || this.parsedFunctions.length === 0) {
|
|
1124
984
|
return;
|
|
@@ -1126,7 +986,7 @@ export class MigrationGenerator {
|
|
|
1126
986
|
|
|
1127
987
|
const definedFunctions = this.parsedFunctions;
|
|
1128
988
|
|
|
1129
|
-
const dbFunctions = await this.
|
|
989
|
+
const dbFunctions = await getDatabaseFunctions(this.knex);
|
|
1130
990
|
const dbFunctionsBySignature = new Map<string, DatabaseFunction>();
|
|
1131
991
|
for (const func of dbFunctions) {
|
|
1132
992
|
dbFunctionsBySignature.set(func.signature, func);
|
|
@@ -1163,12 +1023,10 @@ export class MigrationGenerator {
|
|
|
1163
1023
|
}
|
|
1164
1024
|
});
|
|
1165
1025
|
} else {
|
|
1166
|
-
const dbBody = dbFunc.isAggregate
|
|
1167
|
-
? this.normalizeAggregateDefinition(dbFunc.body)
|
|
1168
|
-
: this.normalizeFunctionBody(dbFunc.body);
|
|
1026
|
+
const dbBody = dbFunc.isAggregate ? normalizeAggregateDefinition(dbFunc.body) : normalizeFunctionBody(dbFunc.body);
|
|
1169
1027
|
const definedBody = definedFunc.isAggregate
|
|
1170
|
-
?
|
|
1171
|
-
:
|
|
1028
|
+
? normalizeAggregateDefinition(definedFunc.body)
|
|
1029
|
+
: normalizeFunctionBody(definedFunc.body);
|
|
1172
1030
|
|
|
1173
1031
|
if (dbBody !== definedBody) {
|
|
1174
1032
|
const oldDefinition = dbFunc.definition || dbFunc.body;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
2
|
import { ParsedFunction } from './types';
|
|
3
3
|
|
|
4
|
-
type DatabaseFunction = {
|
|
4
|
+
export type DatabaseFunction = {
|
|
5
5
|
name: string;
|
|
6
6
|
signature: string;
|
|
7
7
|
body: string;
|
|
@@ -19,11 +19,31 @@ const normalizeWhitespace = (str: string): string => {
|
|
|
19
19
|
.trim();
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const normalizeFunctionBody = (body: string): string => {
|
|
22
|
+
export const normalizeFunctionBody = (body: string): string => {
|
|
23
23
|
return normalizeWhitespace(body);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
export const normalizeAggregateDefinition = (definition: string): string => {
|
|
27
|
+
let normalized = definition
|
|
28
|
+
.replace(/\s+/g, ' ')
|
|
29
|
+
.replace(/\s*\(\s*/g, '(')
|
|
30
|
+
.replace(/\s*\)\s*/g, ')')
|
|
31
|
+
.replace(/\s*,\s*/g, ',')
|
|
32
|
+
.trim();
|
|
33
|
+
|
|
34
|
+
const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
|
|
35
|
+
if (initCondMatch) {
|
|
36
|
+
const initCondValue = initCondMatch[1].trim();
|
|
37
|
+
const unquoted = initCondValue.replace(/^['"]|['"]$/g, '');
|
|
38
|
+
if (/^\d+$/.test(unquoted)) {
|
|
39
|
+
normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return normalized;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const extractFunctionBody = (definition: string): string => {
|
|
27
47
|
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
28
48
|
if (dollarQuoteMatch) {
|
|
29
49
|
return dollarQuoteMatch[2].trim();
|
|
@@ -37,7 +57,7 @@ const extractFunctionBody = (definition: string): string => {
|
|
|
37
57
|
return definition;
|
|
38
58
|
};
|
|
39
59
|
|
|
40
|
-
const getDatabaseFunctions = async (knex: Knex): Promise<DatabaseFunction[]> => {
|
|
60
|
+
export const getDatabaseFunctions = async (knex: Knex): Promise<DatabaseFunction[]> => {
|
|
41
61
|
const regularFunctions = await knex.raw(`
|
|
42
62
|
SELECT
|
|
43
63
|
p.proname as name,
|
|
@@ -108,7 +128,13 @@ const getDatabaseFunctions = async (knex: Knex): Promise<DatabaseFunction[]> =>
|
|
|
108
128
|
}
|
|
109
129
|
|
|
110
130
|
if (initVal !== null && initVal !== undefined) {
|
|
111
|
-
|
|
131
|
+
let initValStr: string;
|
|
132
|
+
if (typeof initVal === 'string') {
|
|
133
|
+
initValStr = `'${initVal}'`;
|
|
134
|
+
} else {
|
|
135
|
+
const numStr = String(initVal);
|
|
136
|
+
initValStr = /^\d+$/.test(numStr) ? `'${numStr}'` : numStr;
|
|
137
|
+
}
|
|
112
138
|
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
113
139
|
}
|
|
114
140
|
|
|
@@ -117,7 +143,7 @@ const getDatabaseFunctions = async (knex: Knex): Promise<DatabaseFunction[]> =>
|
|
|
117
143
|
result.push({
|
|
118
144
|
name,
|
|
119
145
|
signature,
|
|
120
|
-
body:
|
|
146
|
+
body: normalizeAggregateDefinition(aggregateDef),
|
|
121
147
|
isAggregate: true,
|
|
122
148
|
definition: aggregateDef,
|
|
123
149
|
});
|