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'
54
65export default class PostgresMetaTriggers {
76 query : ( sql : string ) => Promise < PostgresMetaResult < any > >
@@ -10,59 +9,216 @@ export default class PostgresMetaTriggers {
109 this . query = query
1110 }
1211
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 )
1614 }
1715
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+ }
66222}
67223
68224const enrichedTriggersSql = `
0 commit comments