1
- import {
2
- triggersSql ,
3
- } from './sql'
4
- import { PostgresMetaResult , PostgresTable } from './types'
1
+ import format , { ident , literal } from 'pg-format'
2
+ import { triggersSql } from './sql'
3
+ import { PostgresMetaResult , PostgresTrigger } from './types'
5
4
6
5
export default class PostgresMetaTriggers {
7
6
query : ( sql : string ) => Promise < PostgresMetaResult < any > >
@@ -10,59 +9,216 @@ export default class PostgresMetaTriggers {
10
9
this . query = query
11
10
}
12
11
13
- async list ( { } = { } ) : Promise < PostgresMetaResult < PostgresTable [ ] > > {
14
- const sql = enrichedTriggersSql
15
- return await this . query ( sql )
12
+ async list ( ) : Promise < PostgresMetaResult < PostgresTrigger [ ] > > {
13
+ return await this . query ( enrichedTriggersSql )
16
14
}
17
15
18
- // async retrieve({ id }: { id: number }): Promise<PostgresMetaResult<PostgresTable>>
19
- // async retrieve({
20
- // name,
21
- // schema,
22
- // }: {
23
- // name: string
24
- // schema: string
25
- // }): Promise<PostgresMetaResult<PostgresTable>>
26
- // async retrieve({
27
- // id,
28
- // name,
29
- // schema = 'public',
30
- // }: {
31
- // id?: number
32
- // name?: string
33
- // schema?: string
34
- // }): Promise<PostgresMetaResult<PostgresTable>> {
35
- // // @TODO
36
- // }
37
-
38
- // async create({
39
- // name,
40
- // schema = 'public',
41
- // comment,
42
- // }: {
43
- // name: string
44
- // schema?: string
45
- // comment?: string
46
- // }): Promise<PostgresMetaResult<PostgresTable>> {
47
- // // @TODO
48
- // }
49
-
50
- // async update(
51
- // id: number,
52
- // {
53
- // name,
54
- // schema,
55
- // }: {
56
- // name?: string
57
- // schema?: string
58
- // }
59
- // ): Promise<PostgresMetaResult<PostgresTable>> {
60
- // // @TODO
61
- // }
62
-
63
- // async remove(id: number, { cascade = false } = {}): Promise<PostgresMetaResult<PostgresTable>> {
64
- // // @TODO
65
- // }
16
+ async retrieve ( { id } : { id : number } ) : Promise < PostgresMetaResult < PostgresTrigger > >
17
+ async retrieve ( {
18
+ name,
19
+ table,
20
+ schema,
21
+ } : {
22
+ name : string
23
+ table : string
24
+ schema ?: string
25
+ } ) : Promise < PostgresMetaResult < PostgresTrigger > >
26
+ async retrieve ( {
27
+ id,
28
+ name,
29
+ schema = 'public' ,
30
+ table,
31
+ } : {
32
+ id ?: number
33
+ name ?: string
34
+ schema ?: string
35
+ table ?: string
36
+ } ) : Promise < PostgresMetaResult < PostgresTrigger > > {
37
+ if ( id ) {
38
+ const sql = `${ enrichedTriggersSql } WHERE triggers.id = ${ literal ( id ) } ;`
39
+
40
+ const { data, error } = await this . query ( sql )
41
+
42
+ if ( error ) {
43
+ return { data : null , error }
44
+ }
45
+
46
+ const triggerRecord = data && data [ 0 ]
47
+
48
+ if ( triggerRecord ) {
49
+ return { data : triggerRecord , error : null }
50
+ }
51
+
52
+ return { data : null , error : { message : `Cannot find a trigger with ID ${ id } ` } }
53
+ }
54
+
55
+ if ( name && schema && table ) {
56
+ const sql = `${ enrichedTriggersSql } WHERE triggers.name = ${ literal (
57
+ name
58
+ ) } AND triggers.schema = ${ literal ( schema ) } AND triggers.table = ${ literal ( table ) } ;`
59
+
60
+ const { data, error } = await this . query ( sql )
61
+
62
+ if ( error ) {
63
+ return { data : null , error }
64
+ }
65
+
66
+ const triggerRecord = data && data [ 0 ]
67
+
68
+ if ( triggerRecord ) {
69
+ return { data : triggerRecord , error : null }
70
+ }
71
+
72
+ return {
73
+ data : null ,
74
+ error : {
75
+ message : `Cannot find a trigger with name ${ name } on table "${ schema } "."${ table } "` ,
76
+ } ,
77
+ }
78
+ }
79
+
80
+ return { data : null , error : { message : 'Invalid parameters on trigger retrieve' } }
81
+ }
82
+
83
+ /**
84
+ * Creates trigger
85
+ *
86
+ * @param {Object } obj - An object.
87
+ * @param {string } obj.name - Trigger name.
88
+ * @param {string } obj.schema - Name of schema that trigger is for.
89
+ * @param {string } obj.table - Unqualified table, view, or foreign table name that trigger is for.
90
+ * @param {string } obj.function_schema - Name of schema that function is for.
91
+ * @param {string } obj.function_name - Unqualified name of the function to execute.
92
+ * @param {('BEFORE'|'AFTER'|'INSTEAD OF') } obj.activation - Determines when function is called
93
+ * during event occurrence.
94
+ * @param {Array<string> } obj.events - Event(s) that will fire the trigger. Array of the following options: 'INSERT' | 'UPDATE' | 'UPDATE
95
+ * OF column_name1,column_name2' | 'DELETE' | 'TRUNCATE'.
96
+ * @param {('ROW'|'STATEMENT') } obj.orientation - Trigger function for every row affected by event or
97
+ * once per statement. Defaults to 'STATEMENT'.
98
+ * @param {string } obj.condition - Boolean expression that will trigger function.
99
+ * For example: '(old.* IS DISTINCT FROM new.*)'
100
+ * @param {Array<string> } obj.function_args - array of arguments to be passed to function when trigger is fired.
101
+ * For example: ['arg1', 'arg2']
102
+ */
103
+ async create ( {
104
+ name,
105
+ schema = 'public' ,
106
+ table,
107
+ function_schema = 'public' ,
108
+ function_name,
109
+ function_args,
110
+ activation,
111
+ events,
112
+ orientation,
113
+ condition,
114
+ } : {
115
+ name : string
116
+ table : string
117
+ function_name : string
118
+ activation : string
119
+ events : string [ ]
120
+ function_schema ?: string
121
+ schema ?: string
122
+ orientation ?: string
123
+ condition ?: string
124
+ function_args ?: string [ ]
125
+ } ) : Promise < PostgresMetaResult < PostgresTrigger > > {
126
+ const qualifiedTableName = `${ ident ( schema ) } .${ ident ( table ) } `
127
+ const qualifiedFunctionName = `${ ident ( function_schema ) } .${ ident ( function_name ) } `
128
+
129
+ const triggerOrientation = orientation ? `FOR EACH ${ format . string ( orientation ) } ` : ''
130
+ const triggerCondition = condition ? `WHEN ( ${ format . string ( condition ) } )` : ''
131
+ const triggerEvents = Array . isArray ( events ) ? `${ format . string ( events . join ( ' OR ' ) ) } ` : ''
132
+ const functionArgs = Array . isArray ( function_args )
133
+ ? `${ function_args . map ( ( arg ) => literal ( arg ) ) . join ( ',' ) } `
134
+ : ''
135
+
136
+ const sql = `CREATE TRIGGER ${ ident ( name ) } ${ format . string (
137
+ activation
138
+ ) } ${ triggerEvents } ON ${ qualifiedTableName } ${ triggerOrientation } ${ triggerCondition } EXECUTE FUNCTION ${ qualifiedFunctionName } ( ${ functionArgs } );`
139
+
140
+ const { error } = await this . query ( sql )
141
+
142
+ if ( error ) {
143
+ return { data : null , error }
144
+ }
145
+
146
+ return await this . retrieve ( {
147
+ name,
148
+ table,
149
+ schema,
150
+ } )
151
+ }
152
+
153
+ async update (
154
+ id : number ,
155
+ {
156
+ name : newName ,
157
+ enabled_mode,
158
+ } : {
159
+ name : string
160
+ enabled_mode : 'ORIGIN' | 'REPLICA' | 'ALWAYS' | 'DISABLED'
161
+ }
162
+ ) : Promise < PostgresMetaResult < PostgresTrigger > > {
163
+ const { data : triggerRecord , error } = await this . retrieve ( { id } )
164
+
165
+ if ( error ) {
166
+ return { data : null , error }
167
+ }
168
+
169
+ let enabledModeSql
170
+ const enabledMode = enabled_mode . toUpperCase ( )
171
+ const { name : currentName , schema : schema , table : table } = triggerRecord !
172
+ const qualifiedTableName = `${ ident ( schema ) } .${ ident ( table ) } `
173
+ const updateNameSql = newName
174
+ ? `ALTER TRIGGER ${ ident ( currentName ) } ON ${ qualifiedTableName } RENAME TO ${ ident ( newName ) } ;`
175
+ : ''
176
+
177
+ if ( [ 'ORIGIN' , 'REPLICA' , 'ALWAYS' , 'DISABLED' ] . includes ( enabledMode ) ) {
178
+ if ( enabledMode === 'DISABLED' ) {
179
+ enabledModeSql = `ALTER TABLE ${ qualifiedTableName } DISABLE TRIGGER ${ ident ( currentName ) } ;`
180
+ } else {
181
+ enabledModeSql = `ALTER TABLE ${ qualifiedTableName } ENABLE ${
182
+ [ 'REPLICA' , 'ALWAYS' ] . includes ( enabledMode ) ? enabledMode : ''
183
+ } TRIGGER ${ ident ( currentName ) } ;`
184
+ }
185
+ }
186
+
187
+ // updateNameSql must be last
188
+ const sql = `BEGIN; ${ enabledModeSql } ${ updateNameSql } COMMIT;`
189
+
190
+ {
191
+ const { error } = await this . query ( sql )
192
+
193
+ if ( error ) {
194
+ return { data : null , error }
195
+ }
196
+ }
197
+
198
+ return await this . retrieve ( { id } )
199
+ }
200
+
201
+ async remove ( id : number , { cascade = false } ) : Promise < PostgresMetaResult < PostgresTrigger > > {
202
+ const { data : triggerRecord , error } = await this . retrieve ( { id } )
203
+
204
+ if ( error ) {
205
+ return { data : null , error }
206
+ }
207
+
208
+ const { name, schema, table } = triggerRecord !
209
+ const qualifiedTableName = `${ ident ( schema ) } .${ ident ( table ) } `
210
+ const sql = `DROP TRIGGER ${ ident ( name ) } ON ${ qualifiedTableName } ${ cascade ? 'CASCADE' : '' } ;`
211
+
212
+ {
213
+ const { error } = await this . query ( sql )
214
+
215
+ if ( error ) {
216
+ return { data : null , error }
217
+ }
218
+ }
219
+
220
+ return { data : triggerRecord ! , error : null }
221
+ }
66
222
}
67
223
68
224
const enrichedTriggersSql = `
0 commit comments