@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/src/resolvers/filters.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { EntityModel, FullContext, getPermissionStack } from '..';
|
|
|
3
3
|
import { ForbiddenError, UserInputError } from '../errors';
|
|
4
4
|
import { OrderBy, Where, normalizeArguments } from './arguments';
|
|
5
5
|
import { FieldResolverNode } from './node';
|
|
6
|
-
import { Joins, QueryBuilderOps, addJoin, apply, applyJoins, getColumn,
|
|
6
|
+
import { Joins, QueryBuilderOps, addJoin, apply, applyJoins, getColumn, ors } from './utils';
|
|
7
7
|
|
|
8
8
|
export const SPECIAL_FILTERS: Record<string, string> = {
|
|
9
9
|
GT: '?? > ?',
|
|
@@ -157,32 +157,19 @@ const applyWhere = (node: FilterNode, where: Where | undefined, ops: QueryBuilde
|
|
|
157
157
|
// Should not happen
|
|
158
158
|
throw new Error(`Invalid filter ${key}.`);
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
const isExpressionField = actualField.generateAs?.type === 'expression';
|
|
162
|
-
const actualColumn = isExpressionField ? getColumnExpression(node, actualKey) : getColumn(node, actualKey);
|
|
163
|
-
if (isExpressionField) {
|
|
164
|
-
const operator = filter === 'GT' ? '>' : filter === 'GTE' ? '>=' : filter === 'LT' ? '<' : '<=';
|
|
165
|
-
ops.push((query) => query.whereRaw(`${actualColumn} ${operator} ?`, [value as string]));
|
|
166
|
-
} else {
|
|
167
|
-
ops.push((query) => query.whereRaw(SPECIAL_FILTERS[filter], [actualColumn, value as string]));
|
|
168
|
-
}
|
|
160
|
+
ops.push((query) => query.whereRaw(SPECIAL_FILTERS[filter], [getColumn(node, actualKey), value as string]));
|
|
169
161
|
continue;
|
|
170
162
|
}
|
|
171
163
|
|
|
172
164
|
const field = node.model.getField(key);
|
|
173
165
|
|
|
174
|
-
const
|
|
175
|
-
const column = isExpressionField ? getColumnExpression(node, key) : getColumn(node, key);
|
|
166
|
+
const column = getColumn(node, key);
|
|
176
167
|
|
|
177
168
|
if (field.kind === 'relation') {
|
|
178
169
|
const relation = node.model.getRelation(field.name);
|
|
179
170
|
|
|
180
171
|
if (value === null) {
|
|
181
|
-
|
|
182
|
-
ops.push((query) => query.whereRaw(`${column} IS NULL`));
|
|
183
|
-
} else {
|
|
184
|
-
ops.push((query) => query.whereNull(column));
|
|
185
|
-
}
|
|
172
|
+
ops.push((query) => query.whereNull(column));
|
|
186
173
|
continue;
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -202,65 +189,35 @@ const applyWhere = (node: FilterNode, where: Where | undefined, ops: QueryBuilde
|
|
|
202
189
|
|
|
203
190
|
if (Array.isArray(value)) {
|
|
204
191
|
if (field && field.list) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
);
|
|
212
|
-
} else {
|
|
213
|
-
ops.push((query) =>
|
|
214
|
-
ors(
|
|
215
|
-
query,
|
|
216
|
-
value.map((v) => (subQuery) => subQuery.whereRaw('? = ANY(??)', [v, column] as string[])),
|
|
217
|
-
),
|
|
218
|
-
);
|
|
219
|
-
}
|
|
192
|
+
ops.push((query) =>
|
|
193
|
+
ors(
|
|
194
|
+
query,
|
|
195
|
+
value.map((v) => (subQuery) => subQuery.whereRaw('? = ANY(??)', [v, column] as string[])),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
220
198
|
continue;
|
|
221
199
|
}
|
|
222
200
|
|
|
223
201
|
if (value.some((v) => v === null)) {
|
|
224
202
|
if (value.some((v) => v !== null)) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
);
|
|
232
|
-
} else {
|
|
233
|
-
ops.push((query) =>
|
|
234
|
-
ors(query, [
|
|
235
|
-
(subQuery) => subQuery.whereIn(column, value.filter((v) => v !== null) as string[]),
|
|
236
|
-
(subQuery) => subQuery.whereNull(column),
|
|
237
|
-
]),
|
|
238
|
-
);
|
|
239
|
-
}
|
|
203
|
+
ops.push((query) =>
|
|
204
|
+
ors(query, [
|
|
205
|
+
(subQuery) => subQuery.whereIn(column, value.filter((v) => v !== null) as string[]),
|
|
206
|
+
(subQuery) => subQuery.whereNull(column),
|
|
207
|
+
]),
|
|
208
|
+
);
|
|
240
209
|
continue;
|
|
241
210
|
}
|
|
242
211
|
|
|
243
|
-
|
|
244
|
-
ops.push((query) => query.whereRaw(`${column} IS NULL`));
|
|
245
|
-
} else {
|
|
246
|
-
ops.push((query) => query.whereNull(column));
|
|
247
|
-
}
|
|
212
|
+
ops.push((query) => query.whereNull(column));
|
|
248
213
|
continue;
|
|
249
214
|
}
|
|
250
215
|
|
|
251
|
-
|
|
252
|
-
ops.push((query) => query.whereRaw(`${column} IN (?)`, [value as string[]]));
|
|
253
|
-
} else {
|
|
254
|
-
ops.push((query) => query.whereIn(column, value as string[]));
|
|
255
|
-
}
|
|
216
|
+
ops.push((query) => query.whereIn(column, value as string[]));
|
|
256
217
|
continue;
|
|
257
218
|
}
|
|
258
219
|
|
|
259
|
-
|
|
260
|
-
ops.push((query) => query.whereRaw(`${column} = ?`, [value]));
|
|
261
|
-
} else {
|
|
262
|
-
ops.push((query) => query.where({ [column]: value }));
|
|
263
|
-
}
|
|
220
|
+
ops.push((query) => query.where({ [column]: value }));
|
|
264
221
|
}
|
|
265
222
|
};
|
|
266
223
|
|
|
@@ -269,18 +226,11 @@ const applySearch = (node: FieldResolverNode, search: string, query: Knex.QueryB
|
|
|
269
226
|
query,
|
|
270
227
|
node.model.fields
|
|
271
228
|
.filter(({ searchable }) => searchable)
|
|
272
|
-
.map(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (isExpressionField) {
|
|
278
|
-
return query.whereRaw(`${column}::text ILIKE ?`, [`%${search}%`]);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return query.whereRaw('??::text ILIKE ?', [column, `%${search}%`]);
|
|
282
|
-
};
|
|
283
|
-
}),
|
|
229
|
+
.map(
|
|
230
|
+
({ name }) =>
|
|
231
|
+
(query) =>
|
|
232
|
+
query.whereRaw('??::text ILIKE ?', [getColumn(node, name), `%${search}%`]),
|
|
233
|
+
),
|
|
284
234
|
);
|
|
285
235
|
|
|
286
236
|
const applyOrderBy = (node: FilterNode, orderBy: OrderBy | OrderBy[], query: Knex.QueryBuilder, joins: Joins) => {
|
|
@@ -311,12 +261,6 @@ const applyOrderBy = (node: FilterNode, orderBy: OrderBy | OrderBy[], query: Kne
|
|
|
311
261
|
}
|
|
312
262
|
|
|
313
263
|
// Simple field
|
|
314
|
-
|
|
315
|
-
const column = isExpressionField ? getColumnExpression(node, key) : getColumn(node, key);
|
|
316
|
-
if (isExpressionField) {
|
|
317
|
-
void query.orderByRaw(`${column} ${value}`);
|
|
318
|
-
} else {
|
|
319
|
-
void query.orderBy(column, value as 'ASC' | 'DESC');
|
|
320
|
-
}
|
|
264
|
+
void query.orderBy(getColumn(node, key), value as 'ASC' | 'DESC');
|
|
321
265
|
}
|
|
322
266
|
};
|
package/src/resolvers/selects.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
isFieldNode,
|
|
14
14
|
} from '.';
|
|
15
15
|
import { PermissionError, UserInputError, getRole } from '..';
|
|
16
|
-
import { getColumnName } from './utils';
|
|
17
16
|
|
|
18
17
|
export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins: Joins) => {
|
|
19
18
|
if (node.isAggregate) {
|
|
@@ -33,15 +32,7 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
|
|
|
33
32
|
...[
|
|
34
33
|
{ tableAlias: node.rootTableAlias, resultAlias: node.resultAlias, field: 'id', fieldAlias: ID_ALIAS },
|
|
35
34
|
...(node.model.root
|
|
36
|
-
? [
|
|
37
|
-
{
|
|
38
|
-
tableAlias: node.rootTableAlias,
|
|
39
|
-
resultAlias: node.resultAlias,
|
|
40
|
-
field: 'type',
|
|
41
|
-
fieldAlias: TYPE_ALIAS,
|
|
42
|
-
generateAs: undefined,
|
|
43
|
-
},
|
|
44
|
-
]
|
|
35
|
+
? [{ tableAlias: node.rootTableAlias, resultAlias: node.resultAlias, field: 'type', fieldAlias: TYPE_ALIAS }]
|
|
45
36
|
: []),
|
|
46
37
|
...getSimpleFields(node)
|
|
47
38
|
.filter((fieldNode) => {
|
|
@@ -78,36 +69,12 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
|
|
|
78
69
|
tableAlias: field.inherited ? node.rootTableAlias : node.tableAlias,
|
|
79
70
|
resultAlias: node.resultAlias,
|
|
80
71
|
fieldAlias,
|
|
81
|
-
generateAs: field.generateAs,
|
|
82
72
|
};
|
|
83
73
|
}),
|
|
84
|
-
].map(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const expression = generateAs.expression.replace(/\b(\w+)\b/g, (match, columnName) => {
|
|
89
|
-
const field = node.model.fields.find((f) => {
|
|
90
|
-
if (f.name === columnName) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
const actualColumnName = getColumnName(f);
|
|
94
|
-
|
|
95
|
-
return actualColumnName === columnName;
|
|
96
|
-
});
|
|
97
|
-
if (field) {
|
|
98
|
-
const actualColumnName = getColumnName(field);
|
|
99
|
-
|
|
100
|
-
return `${tableShortAlias}.${actualColumnName}`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return match;
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
return node.ctx.knex.raw(`(${expression}) as ??`, [`${resultShortAlias}__${fieldAlias}`]);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return `${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`;
|
|
110
|
-
}),
|
|
74
|
+
].map(
|
|
75
|
+
({ tableAlias, resultAlias, field, fieldAlias }) =>
|
|
76
|
+
`${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`,
|
|
77
|
+
),
|
|
111
78
|
);
|
|
112
79
|
|
|
113
80
|
for (const subNode of getInlineFragments(node)) {
|
package/src/resolvers/utils.ts
CHANGED
|
@@ -218,38 +218,6 @@ export const getColumn = (
|
|
|
218
218
|
return `${node.ctx.aliases.getShort(field.inherited ? node.rootTableAlias : node.tableAlias)}.${getColumnName(field)}`;
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
-
export const getColumnExpression = (
|
|
222
|
-
node: Pick<ResolverNode, 'model' | 'ctx' | 'rootTableAlias' | 'tableAlias'>,
|
|
223
|
-
fieldName: string,
|
|
224
|
-
) => {
|
|
225
|
-
const field = node.model.fields.find((field) => field.name === fieldName)!;
|
|
226
|
-
|
|
227
|
-
if (field.generateAs?.type === 'expression') {
|
|
228
|
-
const expression = field.generateAs.expression.replace(/\b(\w+)\b/g, (match, columnName) => {
|
|
229
|
-
const referencedField = node.model.fields.find((f) => {
|
|
230
|
-
if (f.name === columnName) {
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
const actualColumnName = getColumnName(f);
|
|
234
|
-
|
|
235
|
-
return actualColumnName === columnName;
|
|
236
|
-
});
|
|
237
|
-
if (referencedField) {
|
|
238
|
-
const actualColumnName = getColumnName(referencedField);
|
|
239
|
-
const referencedTableAlias = referencedField.inherited ? node.rootTableAlias : node.tableAlias;
|
|
240
|
-
|
|
241
|
-
return `${node.ctx.aliases.getShort(referencedTableAlias)}.${actualColumnName}`;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return match;
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
return `(${expression})`;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return getColumn(node, fieldName);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
221
|
export const getTechnicalDisplay = (model: EntityModel, entity: Entity) =>
|
|
254
222
|
model.displayField && entity[model.displayField]
|
|
255
223
|
? `${model.name} "${entity[model.displayField]}" (${entity.id})`
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export const generateFunctionsFromDatabase = async (knex) => {
|
|
2
|
-
const regularFunctions = await knex.raw(`
|
|
3
|
-
SELECT
|
|
4
|
-
pg_get_functiondef(p.oid) as definition
|
|
5
|
-
FROM pg_proc p
|
|
6
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
7
|
-
WHERE n.nspname = 'public'
|
|
8
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
9
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
10
|
-
`);
|
|
11
|
-
const aggregateFunctions = await knex.raw(`
|
|
12
|
-
SELECT
|
|
13
|
-
p.proname as name,
|
|
14
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
15
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
16
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
17
|
-
a.agginitval as init_val,
|
|
18
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type
|
|
19
|
-
FROM pg_proc p
|
|
20
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
21
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
22
|
-
WHERE n.nspname = 'public'
|
|
23
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
24
|
-
`);
|
|
25
|
-
const functions = [];
|
|
26
|
-
for (const row of regularFunctions.rows || []) {
|
|
27
|
-
if (row.definition) {
|
|
28
|
-
functions.push(row.definition.trim());
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
32
|
-
const name = row.name || '';
|
|
33
|
-
const argumentsStr = row.arguments || '';
|
|
34
|
-
const transFunc = row.trans_func || '';
|
|
35
|
-
const finalFunc = row.final_func || '';
|
|
36
|
-
const initVal = row.init_val;
|
|
37
|
-
const stateType = row.state_type || '';
|
|
38
|
-
if (!name || !transFunc || !stateType) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
let aggregateDef = `CREATE AGGREGATE ${name}(${argumentsStr}) (\n`;
|
|
42
|
-
aggregateDef += ` SFUNC = ${transFunc},\n`;
|
|
43
|
-
aggregateDef += ` STYPE = ${stateType}`;
|
|
44
|
-
if (finalFunc) {
|
|
45
|
-
aggregateDef += `,\n FINALFUNC = ${finalFunc}`;
|
|
46
|
-
}
|
|
47
|
-
if (initVal !== null && initVal !== undefined) {
|
|
48
|
-
const initValStr = typeof initVal === 'string' ? `'${initVal}'` : String(initVal);
|
|
49
|
-
aggregateDef += `,\n INITCOND = ${initValStr}`;
|
|
50
|
-
}
|
|
51
|
-
aggregateDef += '\n);';
|
|
52
|
-
functions.push(aggregateDef);
|
|
53
|
-
}
|
|
54
|
-
if (functions.length === 0) {
|
|
55
|
-
return `export const functions: string[] = [];\n`;
|
|
56
|
-
}
|
|
57
|
-
const functionsArrayString = functions.map((func) => ` ${JSON.stringify(func)}`).join(',\n');
|
|
58
|
-
return `export const functions: string[] = [\n${functionsArrayString},\n];\n`;
|
|
59
|
-
};
|
|
60
|
-
//# sourceMappingURL=generate-functions.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate-functions.js","sourceRoot":"","sources":["../../../src/migrations/generate-functions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,EAAE,IAAU,EAAmB,EAAE;IACjF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;GAQvC,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;GAazC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,kBAAkB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QAEvC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QAED,IAAI,YAAY,GAAG,oBAAoB,IAAI,IAAI,YAAY,OAAO,CAAC;QACnE,YAAY,IAAI,aAAa,SAAS,KAAK,CAAC;QAC5C,YAAY,IAAI,aAAa,SAAS,EAAE,CAAC;QAEzC,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,IAAI,oBAAoB,SAAS,EAAE,CAAC;QAClD,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClF,YAAY,IAAI,mBAAmB,UAAU,EAAE,CAAC;QAClD,CAAC;QAED,YAAY,IAAI,MAAM,CAAC;QAEvB,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,0CAA0C,CAAC;IACpD,CAAC;IAED,MAAM,oBAAoB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE9F,OAAO,yCAAyC,oBAAoB,SAAS,CAAC;AAChF,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/migrations/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
const normalizeWhitespace = (str) => {
|
|
2
|
-
return str
|
|
3
|
-
.replace(/\s+/g, ' ')
|
|
4
|
-
.replace(/\s*\(\s*/g, '(')
|
|
5
|
-
.replace(/\s*\)\s*/g, ')')
|
|
6
|
-
.replace(/\s*,\s*/g, ',')
|
|
7
|
-
.replace(/\s*;\s*/g, ';')
|
|
8
|
-
.trim();
|
|
9
|
-
};
|
|
10
|
-
const normalizeFunctionBody = (body) => {
|
|
11
|
-
return normalizeWhitespace(body);
|
|
12
|
-
};
|
|
13
|
-
const extractFunctionBody = (definition) => {
|
|
14
|
-
const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
|
|
15
|
-
if (dollarQuoteMatch) {
|
|
16
|
-
return dollarQuoteMatch[2].trim();
|
|
17
|
-
}
|
|
18
|
-
const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
|
|
19
|
-
if (bodyMatch) {
|
|
20
|
-
return bodyMatch[1].trim();
|
|
21
|
-
}
|
|
22
|
-
return definition;
|
|
23
|
-
};
|
|
24
|
-
const getDatabaseFunctions = async (knex) => {
|
|
25
|
-
const regularFunctions = await knex.raw(`
|
|
26
|
-
SELECT
|
|
27
|
-
p.proname as name,
|
|
28
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
29
|
-
pg_get_functiondef(p.oid) as definition,
|
|
30
|
-
false as is_aggregate
|
|
31
|
-
FROM pg_proc p
|
|
32
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
33
|
-
WHERE n.nspname = 'public'
|
|
34
|
-
AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
|
|
35
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
36
|
-
`);
|
|
37
|
-
const aggregateFunctions = await knex.raw(`
|
|
38
|
-
SELECT
|
|
39
|
-
p.proname as name,
|
|
40
|
-
pg_get_function_identity_arguments(p.oid) as arguments,
|
|
41
|
-
a.aggtransfn::regproc::text as trans_func,
|
|
42
|
-
a.aggfinalfn::regproc::text as final_func,
|
|
43
|
-
a.agginitval as init_val,
|
|
44
|
-
pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
|
|
45
|
-
true as is_aggregate
|
|
46
|
-
FROM pg_proc p
|
|
47
|
-
JOIN pg_aggregate a ON p.oid = a.aggfnoid
|
|
48
|
-
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
49
|
-
WHERE n.nspname = 'public'
|
|
50
|
-
ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
|
|
51
|
-
`);
|
|
52
|
-
const result = [];
|
|
53
|
-
for (const row of regularFunctions.rows || []) {
|
|
54
|
-
const definition = row.definition || '';
|
|
55
|
-
const name = row.name || '';
|
|
56
|
-
const argumentsStr = row.arguments || '';
|
|
57
|
-
if (!definition) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
const signature = `${name}(${argumentsStr})`;
|
|
61
|
-
const body = normalizeFunctionBody(extractFunctionBody(definition));
|
|
62
|
-
result.push({
|
|
63
|
-
name,
|
|
64
|
-
signature,
|
|
65
|
-
body,
|
|
66
|
-
isAggregate: false,
|
|
67
|
-
definition,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
for (const row of aggregateFunctions.rows || []) {
|
|
71
|
-
const name = row.name || '';
|
|
72
|
-
const argumentsStr = row.arguments || '';
|
|
73
|
-
const transFunc = row.trans_func || '';
|
|
74
|
-
const finalFunc = row.final_func || '';
|
|
75
|
-
const initVal = row.init_val;
|
|
76
|
-
const stateType = row.state_type || '';
|
|
77
|
-
const signature = `${name}(${argumentsStr})`;
|
|
78
|
-
let aggregateDef = `CREATE AGGREGATE ${name}(${argumentsStr}) (`;
|
|
79
|
-
aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
|
|
80
|
-
if (finalFunc) {
|
|
81
|
-
aggregateDef += `, FINALFUNC = ${finalFunc}`;
|
|
82
|
-
}
|
|
83
|
-
if (initVal !== null && initVal !== undefined) {
|
|
84
|
-
const initValStr = typeof initVal === 'string' ? `'${initVal}'` : String(initVal);
|
|
85
|
-
aggregateDef += `, INITCOND = ${initValStr}`;
|
|
86
|
-
}
|
|
87
|
-
aggregateDef += ');';
|
|
88
|
-
result.push({
|
|
89
|
-
name,
|
|
90
|
-
signature,
|
|
91
|
-
body: normalizeFunctionBody(aggregateDef),
|
|
92
|
-
isAggregate: true,
|
|
93
|
-
definition: aggregateDef,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return result;
|
|
97
|
-
};
|
|
98
|
-
const compareFunctions = (defined, db) => {
|
|
99
|
-
const definedBody = normalizeFunctionBody(defined.body);
|
|
100
|
-
const dbBody = normalizeFunctionBody(db.body);
|
|
101
|
-
if (definedBody !== dbBody) {
|
|
102
|
-
const definedPreview = definedBody.length > 200 ? `${definedBody.substring(0, 200)}...` : definedBody;
|
|
103
|
-
const dbPreview = dbBody.length > 200 ? `${dbBody.substring(0, 200)}...` : dbBody;
|
|
104
|
-
return {
|
|
105
|
-
changed: true,
|
|
106
|
-
diff: `Definition changed:\n File: ${definedPreview}\n DB: ${dbPreview}`,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return { changed: false };
|
|
110
|
-
};
|
|
111
|
-
export const updateFunctions = async (knex, parsedFunctions) => {
|
|
112
|
-
if (parsedFunctions.length === 0) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const definedFunctions = parsedFunctions;
|
|
116
|
-
const dbFunctions = await getDatabaseFunctions(knex);
|
|
117
|
-
const dbFunctionsBySignature = new Map();
|
|
118
|
-
for (const func of dbFunctions) {
|
|
119
|
-
dbFunctionsBySignature.set(func.signature, func);
|
|
120
|
-
}
|
|
121
|
-
console.info(`Found ${definedFunctions.length} function(s) in file, ${dbFunctions.length} function(s) in database.`);
|
|
122
|
-
let updatedCount = 0;
|
|
123
|
-
let skippedCount = 0;
|
|
124
|
-
for (const definedFunc of definedFunctions) {
|
|
125
|
-
const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
|
|
126
|
-
if (!dbFunc) {
|
|
127
|
-
try {
|
|
128
|
-
await knex.raw(definedFunc.fullDefinition);
|
|
129
|
-
console.info(`✓ Created ${definedFunc.isAggregate ? 'aggregate' : 'function'}: ${definedFunc.signature}`);
|
|
130
|
-
updatedCount++;
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
console.error(`✗ Failed to create ${definedFunc.isAggregate ? 'aggregate' : 'function'} ${definedFunc.signature}:`, error.message);
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
const comparison = compareFunctions(definedFunc, dbFunc);
|
|
139
|
-
if (comparison.changed) {
|
|
140
|
-
console.info(`\n⚠ ${definedFunc.isAggregate ? 'Aggregate' : 'Function'} ${definedFunc.signature} has changes:`);
|
|
141
|
-
if (comparison.diff) {
|
|
142
|
-
console.info(comparison.diff);
|
|
143
|
-
}
|
|
144
|
-
try {
|
|
145
|
-
if (definedFunc.isAggregate) {
|
|
146
|
-
const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
|
|
147
|
-
if (dropMatch) {
|
|
148
|
-
const functionName = dropMatch[2].trim();
|
|
149
|
-
const argsMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
|
|
150
|
-
const args = argsMatch ? argsMatch[2].trim() : '';
|
|
151
|
-
await knex.raw(`DROP AGGREGATE IF EXISTS ${functionName}${args ? `(${args})` : ''}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
await knex.raw(definedFunc.fullDefinition);
|
|
155
|
-
console.info(`✓ Updated ${definedFunc.isAggregate ? 'aggregate' : 'function'}: ${definedFunc.signature}\n`);
|
|
156
|
-
updatedCount++;
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
console.error(`✗ Failed to update ${definedFunc.isAggregate ? 'aggregate' : 'function'} ${definedFunc.signature}:`, error.message);
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
console.info(`○ Skipped ${definedFunc.isAggregate ? 'aggregate' : 'function'} (unchanged): ${definedFunc.signature}`);
|
|
165
|
-
skippedCount++;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
console.info(`\nSummary: ${updatedCount} updated, ${skippedCount} skipped`);
|
|
170
|
-
if (updatedCount > 0) {
|
|
171
|
-
console.info('Functions updated successfully.');
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
console.info('All functions are up to date.');
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
//# sourceMappingURL=update-functions.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"update-functions.js","sourceRoot":"","sources":["../../../src/migrations/update-functions.ts"],"names":[],"mappings":"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"}
|