@@ -2,7 +2,7 @@ import { Router } from 'express'
2
2
import SQL from 'sql-template-strings'
3
3
import sqlTemplates = require( '../lib/sql' )
4
4
const { columns, grants, policies, primary_keys, relationships, tables } = sqlTemplates
5
- import { coalesceRowsToArray } from '../lib/helpers'
5
+ import { coalesceRowsToArray , toTransaction } from '../lib/helpers'
6
6
import { RunQuery } from '../lib/connectionPool'
7
7
import { DEFAULT_SYSTEM_SCHEMAS } from '../lib/constants'
8
8
import { Tables } from '../lib/interfaces'
@@ -18,7 +18,7 @@ const router = Router()
18
18
19
19
router . get ( '/' , async ( req , res ) => {
20
20
try {
21
- const sql = getTablesSqlize ( { tables, columns, grants, policies, primary_keys, relationships } )
21
+ const sql = getTablesSql ( { tables, columns, grants, policies, primary_keys, relationships } )
22
22
const { data } = await RunQuery ( req . headers . pg , sql )
23
23
const query : QueryParams = req . query
24
24
const includeSystemSchemas = query ?. includeSystemSchemas === 'true'
@@ -40,7 +40,9 @@ router.post('/', async (req, res) => {
40
40
41
41
// Create the table
42
42
const createTableSql = createTable ( name , schema )
43
- await RunQuery ( req . headers . pg , createTableSql )
43
+ const alterSql = alterTableSql ( req . body )
44
+ const transaction = toTransaction ( [ createTableSql , alterSql ] )
45
+ await RunQuery ( req . headers . pg , transaction )
44
46
45
47
// Return fresh details
46
48
const getTable = selectSingleByName ( schema , name )
@@ -57,23 +59,26 @@ router.post('/', async (req, res) => {
57
59
router . patch ( '/:id' , async ( req , res ) => {
58
60
try {
59
61
const id : number = parseInt ( req . params . id )
62
+ if ( ! ( id > 0 ) ) throw new Error ( 'id is required' )
63
+
60
64
const name : string = req . body . name
65
+ const payload : any = { ...req . body }
61
66
62
67
// Get table
63
68
const getTableSql = selectSingleSql ( id )
64
69
const { data : getTableResults } = await RunQuery ( req . headers . pg , getTableSql )
65
70
let previousTable : Tables . Table = getTableResults [ 0 ]
66
71
67
- // Update fields
68
- // NB: Run name updates last
69
- if ( name ) {
70
- const updateName = alterTableName ( previousTable . name , name , previousTable . schema )
71
- await RunQuery ( req . headers . pg , updateName )
72
- }
72
+ // Update fields and name
73
+ const nameSql = ! name ? '' : alterTableName ( previousTable . name , name , previousTable . schema )
74
+ if ( ! name ) payload . name = previousTable . name
75
+ const alterSql = alterTableSql ( payload )
76
+ const transaction = toTransaction ( [ nameSql , alterSql ] )
77
+ await RunQuery ( req . headers . pg , transaction )
73
78
74
79
// Return fresh details
75
- const { data : updatedResults } = await RunQuery ( req . headers . pg , getTableSql )
76
- let updated : Tables . Table = updatedResults [ 0 ]
80
+ const { data : freshTableData } = await RunQuery ( req . headers . pg , getTableSql )
81
+ let updated : Tables . Table = freshTableData [ 0 ]
77
82
return res . status ( 200 ) . json ( updated )
78
83
} catch ( error ) {
79
84
// For this one, we always want to give back the error to the customer
@@ -90,7 +95,7 @@ router.delete('/:id', async (req, res) => {
90
95
const { name, schema } = table
91
96
92
97
const cascade = req . query . cascade === 'true'
93
- const query = dropTableSqlize ( schema , name , cascade )
98
+ const query = dropTableSql ( schema , name , cascade )
94
99
await RunQuery ( req . headers . pg , query )
95
100
96
101
return res . status ( 200 ) . json ( table )
@@ -100,7 +105,7 @@ router.delete('/:id', async (req, res) => {
100
105
}
101
106
} )
102
107
103
- const getTablesSqlize = ( {
108
+ const getTablesSql = ( {
104
109
tables,
105
110
columns,
106
111
grants,
@@ -142,24 +147,51 @@ SELECT
142
147
OR (relationships.target_table_schema = tables.schema AND relationships.target_table_name = tables.name)`
143
148
) }
144
149
FROM
145
- tables`
150
+ tables;` . trim ( )
146
151
}
147
152
const selectSingleSql = ( id : number ) => {
148
- return SQL `` . append ( tables ) . append ( SQL ` and c.oid = ${ id } ` )
153
+ return ` ${ tables } and c.oid = ${ id } ;` . trim ( )
149
154
}
150
155
const selectSingleByName = ( schema : string , name : string ) => {
151
- return SQL `` . append ( tables ) . append ( SQL ` and table_schema = ${ schema } and table_name = ${ name } ` )
156
+ return ` ${ tables } and table_schema = ' ${ schema } ' and table_name = ' ${ name } ';` . trim ( )
152
157
}
153
158
const createTable = ( name : string , schema : string = 'postgres' ) => {
154
- const query = SQL `` . append ( `CREATE TABLE "${ schema } "."${ name } " ()` )
155
- return query
159
+ return `CREATE TABLE IF NOT EXISTS "${ schema } "."${ name } " ();` . trim ( )
156
160
}
157
161
const alterTableName = ( previousName : string , newName : string , schema : string ) => {
158
- const query = SQL `` . append ( `ALTER TABLE "${ schema } "."${ previousName } " RENAME TO "${ newName } "` )
159
- return query
162
+ return `ALTER TABLE "${ schema } "."${ previousName } " RENAME TO "${ newName } ";` . trim ( )
163
+ }
164
+ const alterTableSql = ( {
165
+ schema = 'public' ,
166
+ name,
167
+ rls_enabled,
168
+ rls_forced,
169
+ } : {
170
+ schema ?: string
171
+ name : string
172
+ rls_enabled ?: boolean
173
+ rls_forced ?: boolean
174
+ } ) => {
175
+ let alter = `ALTER table "${ schema } "."${ name } "`
176
+ let enableRls = ''
177
+ if ( rls_enabled !== undefined ) {
178
+ let enable = `${ alter } ENABLE ROW LEVEL SECURITY;`
179
+ let disable = `${ alter } DISABLE ROW LEVEL SECURITY;`
180
+ enableRls = rls_enabled ? enable : disable
181
+ }
182
+ let forceRls = ''
183
+ if ( rls_forced !== undefined ) {
184
+ let enable = `${ alter } FORCE ROW LEVEL SECURITY;`
185
+ let disable = `${ alter } NO FORCE ROW LEVEL SECURITY;`
186
+ forceRls = rls_forced ? enable : disable
187
+ }
188
+ return `
189
+ ${ enableRls }
190
+ ${ forceRls }
191
+ ` . trim ( )
160
192
}
161
- const dropTableSqlize = ( schema : string , name : string , cascade : boolean ) => {
162
- return `DROP TABLE "${ schema } "."${ name } " ${ cascade ? 'CASCADE' : 'RESTRICT' } `
193
+ const dropTableSql = ( schema : string , name : string , cascade : boolean ) => {
194
+ return `DROP TABLE "${ schema } "."${ name } " ${ cascade ? 'CASCADE' : 'RESTRICT' } ;` . trim ( )
163
195
}
164
196
const removeSystemSchemas = ( data : Tables . Table [ ] ) => {
165
197
return data . filter ( ( x ) => ! DEFAULT_SYSTEM_SCHEMAS . includes ( x . schema ) )
0 commit comments