@smartive/graphql-magic 23.4.0-next.9 → 23.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -3
- package/dist/bin/gqm.cjs +125 -656
- package/dist/cjs/index.cjs +2118 -2685
- package/dist/esm/migrations/generate.d.ts +1 -9
- package/dist/esm/migrations/generate.js +33 -269
- package/dist/esm/migrations/generate.js.map +1 -1
- package/dist/esm/migrations/index.d.ts +0 -2
- package/dist/esm/migrations/index.js +0 -2
- package/dist/esm/migrations/index.js.map +1 -1
- package/dist/esm/models/model-definitions.d.ts +1 -4
- package/dist/esm/resolvers/filters.js +14 -76
- package/dist/esm/resolvers/filters.js.map +1 -1
- package/dist/esm/resolvers/selects.js +2 -20
- package/dist/esm/resolvers/selects.js.map +1 -1
- package/dist/esm/resolvers/utils.d.ts +0 -1
- package/dist/esm/resolvers/utils.js +0 -29
- package/dist/esm/resolvers/utils.js.map +1 -1
- package/docs/docs/3-fields.md +0 -149
- package/docs/docs/5-migrations.md +1 -9
- package/package.json +2 -2
- package/src/bin/gqm/gqm.ts +5 -44
- package/src/bin/gqm/settings.ts +0 -7
- package/src/bin/gqm/static-eval.ts +102 -0
- package/src/bin/gqm/utils.ts +0 -1
- package/src/migrations/generate.ts +41 -334
- package/src/migrations/index.ts +0 -2
- package/src/models/model-definitions.ts +1 -4
- package/src/resolvers/filters.ts +25 -88
- package/src/resolvers/selects.ts +5 -22
- package/src/resolvers/utils.ts +0 -44
- package/dist/esm/migrations/generate-functions.d.ts +0 -2
- package/dist/esm/migrations/generate-functions.js +0 -60
- package/dist/esm/migrations/generate-functions.js.map +0 -1
- package/dist/esm/migrations/types.d.ts +0 -7
- package/dist/esm/migrations/types.js +0 -2
- package/dist/esm/migrations/types.js.map +0 -1
- package/dist/esm/migrations/update-functions.d.ts +0 -3
- package/dist/esm/migrations/update-functions.js +0 -177
- package/dist/esm/migrations/update-functions.js.map +0 -1
- package/src/bin/gqm/parse-functions.ts +0 -141
- package/src/migrations/generate-functions.ts +0 -74
- package/src/migrations/types.ts +0 -7
- package/src/migrations/update-functions.ts +0 -221
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,72 +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
|
-
.filter((v) => v !== null)
|
|
232
|
-
.map(() => `?`)
|
|
233
|
-
.join(',')})`,
|
|
234
|
-
value.filter((v) => v !== null) as string[],
|
|
235
|
-
),
|
|
236
|
-
(subQuery) => subQuery.whereRaw(`${column} IS NULL`),
|
|
237
|
-
]),
|
|
238
|
-
);
|
|
239
|
-
} else {
|
|
240
|
-
ops.push((query) =>
|
|
241
|
-
ors(query, [
|
|
242
|
-
(subQuery) => subQuery.whereIn(column, value.filter((v) => v !== null) as string[]),
|
|
243
|
-
(subQuery) => subQuery.whereNull(column),
|
|
244
|
-
]),
|
|
245
|
-
);
|
|
246
|
-
}
|
|
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
|
+
);
|
|
247
209
|
continue;
|
|
248
210
|
}
|
|
249
211
|
|
|
250
|
-
|
|
251
|
-
ops.push((query) => query.whereRaw(`${column} IS NULL`));
|
|
252
|
-
} else {
|
|
253
|
-
ops.push((query) => query.whereNull(column));
|
|
254
|
-
}
|
|
212
|
+
ops.push((query) => query.whereNull(column));
|
|
255
213
|
continue;
|
|
256
214
|
}
|
|
257
215
|
|
|
258
|
-
|
|
259
|
-
ops.push((query) => query.whereRaw(`${column} IN (${value.map(() => `?`).join(',')})`, value as string[]));
|
|
260
|
-
} else {
|
|
261
|
-
ops.push((query) => query.whereIn(column, value as string[]));
|
|
262
|
-
}
|
|
216
|
+
ops.push((query) => query.whereIn(column, value as string[]));
|
|
263
217
|
continue;
|
|
264
218
|
}
|
|
265
219
|
|
|
266
|
-
|
|
267
|
-
ops.push((query) => query.whereRaw(`${column} = ?`, [value]));
|
|
268
|
-
} else {
|
|
269
|
-
ops.push((query) => query.where({ [column]: value }));
|
|
270
|
-
}
|
|
220
|
+
ops.push((query) => query.where({ [column]: value }));
|
|
271
221
|
}
|
|
272
222
|
};
|
|
273
223
|
|
|
@@ -276,18 +226,11 @@ const applySearch = (node: FieldResolverNode, search: string, query: Knex.QueryB
|
|
|
276
226
|
query,
|
|
277
227
|
node.model.fields
|
|
278
228
|
.filter(({ searchable }) => searchable)
|
|
279
|
-
.map(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (isExpressionField) {
|
|
285
|
-
return query.whereRaw(`${column}::text ILIKE ?`, [`%${search}%`]);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return query.whereRaw('??::text ILIKE ?', [column, `%${search}%`]);
|
|
289
|
-
};
|
|
290
|
-
}),
|
|
229
|
+
.map(
|
|
230
|
+
({ name }) =>
|
|
231
|
+
(query) =>
|
|
232
|
+
query.whereRaw('??::text ILIKE ?', [getColumn(node, name), `%${search}%`]),
|
|
233
|
+
),
|
|
291
234
|
);
|
|
292
235
|
|
|
293
236
|
const applyOrderBy = (node: FilterNode, orderBy: OrderBy | OrderBy[], query: Knex.QueryBuilder, joins: Joins) => {
|
|
@@ -318,12 +261,6 @@ const applyOrderBy = (node: FilterNode, orderBy: OrderBy | OrderBy[], query: Kne
|
|
|
318
261
|
}
|
|
319
262
|
|
|
320
263
|
// Simple field
|
|
321
|
-
|
|
322
|
-
const column = isExpressionField ? getColumnExpression(node, key) : getColumn(node, key);
|
|
323
|
-
if (isExpressionField) {
|
|
324
|
-
void query.orderByRaw(`${column} ${value}`);
|
|
325
|
-
} else {
|
|
326
|
-
void query.orderBy(column, value as 'ASC' | 'DESC');
|
|
327
|
-
}
|
|
264
|
+
void query.orderBy(getColumn(node, key), value as 'ASC' | 'DESC');
|
|
328
265
|
}
|
|
329
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 { getColumnExpression } 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,20 +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 = columnExpression.slice(1, -1);
|
|
89
|
-
|
|
90
|
-
return node.ctx.knex.raw(`(${expression}) as ??`, [`${resultShortAlias}__${fieldAlias}`]);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return `${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`;
|
|
94
|
-
}),
|
|
74
|
+
].map(
|
|
75
|
+
({ tableAlias, resultAlias, field, fieldAlias }) =>
|
|
76
|
+
`${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`,
|
|
77
|
+
),
|
|
95
78
|
);
|
|
96
79
|
|
|
97
80
|
for (const subNode of getInlineFragments(node)) {
|
package/src/resolvers/utils.ts
CHANGED
|
@@ -218,50 +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
|
-
const replaceColumnReferences = (
|
|
222
|
-
expression: string,
|
|
223
|
-
node: Pick<ResolverNode, 'model' | 'ctx' | 'rootTableAlias' | 'tableAlias'>,
|
|
224
|
-
) => {
|
|
225
|
-
const replaceField = (columnName: string) => {
|
|
226
|
-
const referencedField = node.model.fields.find((f) => getColumnName(f) === columnName);
|
|
227
|
-
if (referencedField) {
|
|
228
|
-
const actualColumnName = getColumnName(referencedField);
|
|
229
|
-
const referencedTableAlias = referencedField.inherited ? node.rootTableAlias : node.tableAlias;
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
tableAlias: node.ctx.aliases.getShort(referencedTableAlias),
|
|
233
|
-
columnName: actualColumnName,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return null;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
return expression.replace(/"(\w+)"/g, (match, columnName) => {
|
|
241
|
-
const replacement = replaceField(columnName);
|
|
242
|
-
if (replacement) {
|
|
243
|
-
return `"${replacement.tableAlias}"."${replacement.columnName}"`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return match;
|
|
247
|
-
});
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
export const getColumnExpression = (
|
|
251
|
-
node: Pick<ResolverNode, 'model' | 'ctx' | 'rootTableAlias' | 'tableAlias'>,
|
|
252
|
-
fieldName: string,
|
|
253
|
-
) => {
|
|
254
|
-
const field = node.model.fields.find((field) => field.name === fieldName)!;
|
|
255
|
-
|
|
256
|
-
if (field.generateAs?.type === 'expression') {
|
|
257
|
-
const expression = replaceColumnReferences(field.generateAs.expression, node);
|
|
258
|
-
|
|
259
|
-
return `(${expression})`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return getColumn(node, fieldName);
|
|
263
|
-
};
|
|
264
|
-
|
|
265
221
|
export const getTechnicalDisplay = (model: EntityModel, entity: Entity) =>
|
|
266
222
|
model.displayField && entity[model.displayField]
|
|
267
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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"}
|