11import { Router } from 'express'
2- import format , { literal } from 'pg-format'
2+ import format , { ident , literal } from 'pg-format'
33import SQL from 'sql-template-strings'
44import { RunQuery } from '../lib/connectionPool'
55import sql = require( '../lib/sql' )
@@ -59,13 +59,9 @@ router.patch('/:id', async (req, res) => {
5959 const [ tableId , ordinalPos ] = req . params . id . split ( '.' ) . map ( Number )
6060 const getColumnQuery = getColumnByPosSqlize ( tableId , ordinalPos )
6161 const column = ( await RunQuery ( req . headers . pg , getColumnQuery ) ) . data [ 0 ]
62- const { schema, table, name : oldName } = column
6362
6463 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 )
6965 await RunQuery ( req . headers . pg , query )
7066
7167 const updated = ( await RunQuery ( req . headers . pg , getColumnQuery ) ) . data [ 0 ]
@@ -104,6 +100,7 @@ const addColumnSqlize = ({
104100 default_value,
105101 default_value_format = 'literal' ,
106102 is_identity = false ,
103+ identity_generation,
107104 is_nullable = true ,
108105 is_primary_key = false ,
109106 is_unique = false ,
@@ -116,6 +113,7 @@ const addColumnSqlize = ({
116113 default_value ?: any
117114 default_value_format ?: 'expression' | 'literal'
118115 is_identity ?: boolean
116+ identity_generation ?: 'BY DEFAULT' | 'ALWAYS'
119117 is_nullable ?: boolean
120118 is_primary_key ?: boolean
121119 is_unique ?: boolean
@@ -129,7 +127,7 @@ const addColumnSqlize = ({
129127 } else {
130128 defaultValueSql = `DEFAULT ${ literal ( default_value ) } `
131129 }
132- const isIdentitySql = is_identity ? ' GENERATED BY DEFAULT AS IDENTITY' : ''
130+ const isIdentitySql = is_identity ? ` GENERATED ${ identity_generation } AS IDENTITY` : ''
133131 const isNullableSql = is_nullable ? 'NULL' : 'NOT NULL'
134132 const isPrimaryKeySql = is_primary_key ? 'PRIMARY KEY' : ''
135133 const isUniqueSql = is_unique ? 'UNIQUE' : ''
@@ -161,81 +159,113 @@ const getColumnByPosSqlize = (tableId: number, ordinalPos: number) => {
161159 . append ( columns )
162160 . append ( SQL ` WHERE c.oid = ${ tableId } AND ordinal_position = ${ ordinalPos } ` )
163161}
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+ ) => {
187186 const nameSql =
188- name === undefined || name === oldName
187+ name === undefined || name === old . name
189188 ? ''
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 )
191190 // We use USING to allow implicit conversion of incompatible types (e.g. int4 -> text).
192191 const typeSql =
193192 type === undefined
194193 ? ''
195194 : format (
196195 '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 ,
200199 type ,
201- oldName ,
200+ old . name ,
202201 type
203202 )
204- let defaultValueSql = ''
203+ let defaultValueSql : string
205204 if ( drop_default ) {
206205 defaultValueSql = format (
207206 'ALTER TABLE %I.%I ALTER COLUMN %I DROP DEFAULT;' ,
208- schema ,
209- table ,
210- oldName
207+ old . schema ,
208+ old . table ,
209+ old . name
211210 )
212- } else if ( default_value !== undefined ) {
211+ } else if ( default_value === undefined ) {
212+ defaultValueSql = ''
213+ } else {
213214 let defaultValue =
214215 default_value_format === 'expression' ? default_value : literal ( default_value )
215216 defaultValueSql = format (
216217 `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
220221 )
221222 }
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 {
224251 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 )
227254 }
228255 const commentSql =
229256 comment === undefined
230257 ? ''
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 )
232259
233260 // 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).
234263 return `
235264BEGIN;
236265 ${ isNullableSql }
237266 ${ defaultValueSql }
238267 ${ typeSql }
268+ ${ identitySql }
239269 ${ commentSql }
240270 ${ nameSql }
241271COMMIT;`
0 commit comments