-
Notifications
You must be signed in to change notification settings - Fork 180
/
createTrigger.ts
120 lines (100 loc) · 3.55 KB
/
createTrigger.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
119
120
import type { MigrationOptions } from '../../types';
import { escapeValue, toArray } from '../../utils';
import type { FunctionOptions } from '../functions';
import { createFunction, dropFunction } from '../functions';
import type { DropOptions, Name, Reversible, Value } from '../generalTypes';
import type { DropTriggerOptions } from './dropTrigger';
import { dropTrigger } from './dropTrigger';
import type { TriggerOptions } from './shared';
export type CreateTriggerFn1 = (
tableName: Name,
triggerName: string,
triggerOptions: TriggerOptions & DropTriggerOptions
) => string;
export type CreateTriggerFn2 = (
tableName: Name,
triggerName: string,
triggerOptions: TriggerOptions & FunctionOptions & DropTriggerOptions,
definition: Value
) => string;
export type CreateTriggerFn = CreateTriggerFn1 | CreateTriggerFn2;
export type CreateTrigger = Reversible<CreateTriggerFn>;
export function createTrigger(mOptions: MigrationOptions): CreateTrigger {
const _create: CreateTrigger = (
tableName: Name,
triggerName: string,
triggerOptions:
| (TriggerOptions & DropOptions)
| (TriggerOptions & FunctionOptions & DropOptions),
definition?: Value
) => {
const {
constraint = false,
condition,
operation,
deferrable = false,
deferred = false,
functionParams = [],
} = triggerOptions;
let { when, level = 'STATEMENT', function: functionName } = triggerOptions;
const operations = toArray(operation).join(' OR ');
if (constraint) {
when = 'AFTER';
}
if (!when) {
throw new Error('"when" (BEFORE/AFTER/INSTEAD OF) have to be specified');
}
const isInsteadOf = /instead\s+of/i.test(when);
if (isInsteadOf) {
level = 'ROW';
}
if (definition) {
functionName = functionName === undefined ? triggerName : functionName;
}
if (!functionName) {
throw new Error("Can't determine function name");
}
if (isInsteadOf && condition) {
throw new Error("INSTEAD OF trigger can't have condition specified");
}
if (!operations) {
throw new Error(
'"operation" (INSERT/UPDATE[ OF ...]/DELETE/TRUNCATE) have to be specified'
);
}
const defferStr = constraint
? `${deferrable ? `DEFERRABLE INITIALLY ${deferred ? 'DEFERRED' : 'IMMEDIATE'}` : 'NOT DEFERRABLE'}\n `
: '';
const conditionClause = condition ? `WHEN (${condition})\n ` : '';
const constraintStr = constraint ? ' CONSTRAINT' : '';
const paramsStr = functionParams.map(escapeValue).join(', ');
const triggerNameStr = mOptions.literal(triggerName);
const tableNameStr = mOptions.literal(tableName);
const functionNameStr = mOptions.literal(functionName);
const triggerSQL = `CREATE${constraintStr} TRIGGER ${triggerNameStr}
${when} ${operations} ON ${tableNameStr}
${defferStr}FOR EACH ${level}
${conditionClause}EXECUTE PROCEDURE ${functionNameStr}(${paramsStr});`;
const fnSQL = definition
? `${createFunction(mOptions)(
functionName,
[],
{ ...(triggerOptions as FunctionOptions), returns: 'trigger' },
definition
)}\n`
: '';
return `${fnSQL}${triggerSQL}`;
};
_create.reverse = (tableName, triggerName, triggerOptions, definition) => {
const triggerSQL = dropTrigger(mOptions)(
tableName,
triggerName,
triggerOptions
);
const fnSQL = definition
? `\n${dropFunction(mOptions)(triggerOptions.function || triggerName, [], triggerOptions)}`
: '';
return `${triggerSQL}${fnSQL}`;
};
return _create;
}