deno.land / x / pg_mem@2.8.1 / execution / schema-amends / create-function.ts

create-function.ts
نووسراو ببینە
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { _Transaction, _ISchema, NotSupported, _ITable, _IStatement, _IStatementExecutor, QueryError, _ArgDefDetails, IType, _IType, nil, FunctionDefinition } from '../../interfaces-private.ts';import { CreateFunctionStatement } from 'https://deno.land/x/pgsql_ast_parser@12.0.1/mod.ts';import { ExecHelper } from '../exec-utils.ts';import { buildValue } from '../../parser/expression-builder.ts';import { Types } from '../../datatypes/index.ts';import { ignore, deepEqual } from '../../utils.ts';import { withSelection } from '../../parser/context.ts';
export class CreateFunction extends ExecHelper implements _IStatementExecutor { private onSchema: _ISchema; private toRegister: FunctionDefinition; private replace: boolean;
constructor({ schema }: _IStatement, fn: CreateFunctionStatement) { super(fn); if (!fn.language) { throw new QueryError('Unspecified function language'); } this.onSchema = schema.getThisOrSiblingFor(fn.name);
const lang = schema.db.getLanguage(fn.language.name);
// determine arg types const args = withSelection(schema.dualTable.selection, () => fn.arguments.map<_ArgDefDetails>(a => ({ name: a.name?.name, type: schema.getType(a.type), default: a.default && buildValue(a.default), mode: a.mode, })));
// determine return type let returns: IType | null = null; if (!fn.returns) { throw new QueryError('Unspecified function return type'); } if (typeof fn.code !== 'string') { throw new QueryError('no function body specified'); } switch (fn.returns.kind) { case 'table': const columns = fn.returns.columns.map(c => ({ name: c.name.name, type: schema.getType(c.type), })); returns = Types.record(columns).asArray(); break; case 'array': case null: case undefined: returns = schema.getType(fn.returns); break; default: throw NotSupported.never(fn.returns); }
let argsVariadic: _IType | nil; const variad = args.filter(x => x.mode === 'variadic'); if (variad.length > 1) { throw new QueryError(`Expected only one "VARIADIC" argument`); } else if (variad.length) { argsVariadic = variad[0].type; }
// compile & register the associated function const compiled = lang({ args, code: fn.code, returns, functionName: fn.name.name, schema: schema, });
this.toRegister = { name: fn.name.name, returns, implementation: compiled, args: args.filter(x => x.mode !== 'variadic'), argsVariadic, impure: fn.purity !== 'immutable', allowNullArguments: fn.onNullInput === 'call', }; this.replace = fn.orReplace ?? false;
// if the function exists const existing = this.onSchema.getFunction(this.toRegister.name, args.map(x => x.type)); if (existing) { if (!this.replace) { throw new QueryError(`function ${this.toRegister.name} lready exists with same argument types`, '42723'); }
// ... it must be the same type if (existing.returns !== returns) { throw new QueryError(`cannot change return type of existing function`, '42P13'); }
// ... argument names must be the same for (let i = 0; i < args.length; i++) { const exName = existing.args[i].name if (exName ?? null !== args[i].name ?? null) { throw new QueryError(`cannot change name of input parameter "${exName}"`, '42P13'); } } } }
execute(t: _Transaction) { // commit pending data before making changes // (because does not support further rollbacks) t = t.fullCommit();
this.onSchema.registerFunction(this.toRegister, this.replace);
// new implicit transaction t = t.fork(); return this.noData(t, 'CREATE'); }}
pg_mem

Version Info

Tagged at
a year ago