1
1
import { Router } from 'express'
2
- import format , { literal } from 'pg-format'
2
+ import format , { ident , literal } from 'pg-format'
3
3
import SQL from 'sql-template-strings'
4
4
import { RunQuery } from '../lib/connectionPool'
5
5
import sql = require( '../lib/sql' )
@@ -59,13 +59,9 @@ router.patch('/:id', async (req, res) => {
59
59
const [ tableId , ordinalPos ] = req . params . id . split ( '.' ) . map ( Number )
60
60
const getColumnQuery = getColumnByPosSqlize ( tableId , ordinalPos )
61
61
const column = ( await RunQuery ( req . headers . pg , getColumnQuery ) ) . data [ 0 ]
62
- const { schema, table, name : oldName } = column
63
62
64
63
const alterColumnArgs = req . body
65
- alterColumnArgs . schema = schema
66
- alterColumnArgs . table = table
67
- alterColumnArgs . oldName = oldName
68
- const query = alterColumnSqlize ( alterColumnArgs )
64
+ const query = alterColumnSqlize ( column , alterColumnArgs )
69
65
await RunQuery ( req . headers . pg , query )
70
66
71
67
const updated = ( await RunQuery ( req . headers . pg , getColumnQuery ) ) . data [ 0 ]
@@ -104,6 +100,7 @@ const addColumnSqlize = ({
104
100
default_value,
105
101
default_value_format = 'literal' ,
106
102
is_identity = false ,
103
+ identity_generation,
107
104
is_nullable = true ,
108
105
is_primary_key = false ,
109
106
is_unique = false ,
@@ -116,6 +113,7 @@ const addColumnSqlize = ({
116
113
default_value ?: any
117
114
default_value_format ?: 'expression' | 'literal'
118
115
is_identity ?: boolean
116
+ identity_generation ?: 'BY DEFAULT' | 'ALWAYS'
119
117
is_nullable ?: boolean
120
118
is_primary_key ?: boolean
121
119
is_unique ?: boolean
@@ -129,7 +127,7 @@ const addColumnSqlize = ({
129
127
} else {
130
128
defaultValueSql = `DEFAULT ${ literal ( default_value ) } `
131
129
}
132
- const isIdentitySql = is_identity ? ' GENERATED BY DEFAULT AS IDENTITY' : ''
130
+ const isIdentitySql = is_identity ? ` GENERATED ${ identity_generation } AS IDENTITY` : ''
133
131
const isNullableSql = is_nullable ? 'NULL' : 'NOT NULL'
134
132
const isPrimaryKeySql = is_primary_key ? 'PRIMARY KEY' : ''
135
133
const isUniqueSql = is_unique ? 'UNIQUE' : ''
@@ -161,81 +159,113 @@ const getColumnByPosSqlize = (tableId: number, ordinalPos: number) => {
161
159
. append ( columns )
162
160
. append ( SQL ` WHERE c.oid = ${ tableId } AND ordinal_position = ${ ordinalPos } ` )
163
161
}
164
- const alterColumnSqlize = ( {
165
- schema,
166
- table,
167
- oldName,
168
- name,
169
- type,
170
- drop_default = false ,
171
- default_value,
172
- default_value_format = 'literal' ,
173
- is_nullable,
174
- comment,
175
- } : {
176
- schema : string
177
- table : string
178
- oldName : string
179
- name ?: string
180
- type ?: string
181
- drop_default ?: boolean
182
- default_value ?: any
183
- default_value_format ?: 'expression' | 'literal'
184
- is_nullable ?: boolean
185
- comment ?: string
186
- } ) => {
162
+ const alterColumnSqlize = (
163
+ old : any ,
164
+ {
165
+ name,
166
+ type,
167
+ drop_default = false ,
168
+ default_value,
169
+ default_value_format = 'literal' ,
170
+ is_identity,
171
+ identity_generation,
172
+ is_nullable,
173
+ comment,
174
+ } : {
175
+ name ?: string
176
+ type ?: string
177
+ drop_default ?: boolean
178
+ default_value ?: any
179
+ default_value_format ?: 'expression' | 'literal'
180
+ is_identity ?: boolean
181
+ identity_generation ?: 'BY DEFAULT' | 'ALWAYS'
182
+ is_nullable ?: boolean
183
+ comment ?: string
184
+ }
185
+ ) => {
187
186
const nameSql =
188
- name === undefined || name === oldName
187
+ name === undefined || name === old . name
189
188
? ''
190
- : format ( 'ALTER TABLE %I.%I RENAME COLUMN %I TO %I;' , schema , table , oldName , name )
189
+ : format ( 'ALTER TABLE %I.%I RENAME COLUMN %I TO %I;' , old . schema , old . table , old . name , name )
191
190
// We use USING to allow implicit conversion of incompatible types (e.g. int4 -> text).
192
191
const typeSql =
193
192
type === undefined
194
193
? ''
195
194
: format (
196
195
'ALTER TABLE %I.%I ALTER COLUMN %I SET DATA TYPE %I USING %I::%I;' ,
197
- schema ,
198
- table ,
199
- oldName ,
196
+ old . schema ,
197
+ old . table ,
198
+ old . name ,
200
199
type ,
201
- oldName ,
200
+ old . name ,
202
201
type
203
202
)
204
- let defaultValueSql = ''
203
+ let defaultValueSql : string
205
204
if ( drop_default ) {
206
205
defaultValueSql = format (
207
206
'ALTER TABLE %I.%I ALTER COLUMN %I DROP DEFAULT;' ,
208
- schema ,
209
- table ,
210
- oldName
207
+ old . schema ,
208
+ old . table ,
209
+ old . name
211
210
)
212
- } else if ( default_value !== undefined ) {
211
+ } else if ( default_value === undefined ) {
212
+ defaultValueSql = ''
213
+ } else {
213
214
let defaultValue =
214
215
default_value_format === 'expression' ? default_value : literal ( default_value )
215
216
defaultValueSql = format (
216
217
`ALTER TABLE %I.%I ALTER COLUMN %I SET DEFAULT ${ defaultValue } ;` ,
217
- schema ,
218
- table ,
219
- oldName
218
+ old . schema ,
219
+ old . table ,
220
+ old . name
220
221
)
221
222
}
222
- let isNullableSql = ''
223
- if ( is_nullable !== undefined ) {
223
+ // What identitySql does vary depending on the old and new values of
224
+ // is_identity and identity_generation.
225
+ //
226
+ // | is_identity: old \ new | undefined | true | false |
227
+ // |------------------------+--------------------+--------------------+----------------|
228
+ // | true | maybe set identity | maybe set identity | drop if exists |
229
+ // |------------------------+--------------------+--------------------+----------------|
230
+ // | false | - | add identity | drop if exists |
231
+ let identitySql = `ALTER TABLE ${ ident ( old . schema ) } .${ ident ( old . table ) } ALTER COLUMN ${ ident (
232
+ old . name
233
+ ) } `
234
+ if ( is_identity === false ) {
235
+ identitySql += 'DROP IDENTITY IF EXISTS;'
236
+ } else if ( old . is_identity === true ) {
237
+ if ( identity_generation === undefined ) {
238
+ identitySql = ''
239
+ } else {
240
+ identitySql += `SET GENERATED ${ identity_generation } ;`
241
+ }
242
+ } else if ( is_identity === undefined ) {
243
+ identitySql = ''
244
+ } else {
245
+ identitySql += `ADD GENERATED ${ identity_generation } AS IDENTITY;`
246
+ }
247
+ let isNullableSql : string
248
+ if ( is_nullable === undefined ) {
249
+ isNullableSql = ''
250
+ } else {
224
251
isNullableSql = is_nullable
225
- ? format ( 'ALTER TABLE %I.%I ALTER COLUMN %I DROP NOT NULL;' , schema , table , oldName )
226
- : format ( 'ALTER TABLE %I.%I ALTER COLUMN %I SET NOT NULL;' , schema , table , oldName )
252
+ ? format ( 'ALTER TABLE %I.%I ALTER COLUMN %I DROP NOT NULL;' , old . schema , old . table , old . name )
253
+ : format ( 'ALTER TABLE %I.%I ALTER COLUMN %I SET NOT NULL;' , old . schema , old . table , old . name )
227
254
}
228
255
const commentSql =
229
256
comment === undefined
230
257
? ''
231
- : format ( 'COMMENT ON COLUMN %I.%I.%I IS %L;' , schema , table , oldName , comment )
258
+ : format ( 'COMMENT ON COLUMN %I.%I.%I IS %L;' , old . schema , old . table , old . name , comment )
232
259
233
260
// nameSql must be last.
261
+ // TODO: Can't set default if column is previously identity even if is_identity: false.
262
+ // Must do two separate PATCHes (once to drop identity and another to set default).
234
263
return `
235
264
BEGIN;
236
265
${ isNullableSql }
237
266
${ defaultValueSql }
238
267
${ typeSql }
268
+ ${ identitySql }
239
269
${ commentSql }
240
270
${ nameSql }
241
271
COMMIT;`
0 commit comments