Skip to content

Commit 17e8ef2

Browse files
committed
feat: more options on /column POST & PATCH
Still missing some parameters (e.g. PATCH identity), but good enough for most use cases.
1 parent 667e6b6 commit 17e8ef2

File tree

2 files changed

+86
-22
lines changed

2 files changed

+86
-22
lines changed

src/api/columns.ts

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ router.get('/', async (req, res) => {
3131

3232
router.post('/', async (req, res) => {
3333
try {
34-
const { tableId, name, type } = req.body as {
35-
tableId: number
36-
name: string
37-
type: string
38-
}
34+
const tableId: number = req.body.tableId
35+
const name: string = req.body.name
3936
const getTableQuery = getTableSqlize(tableId)
4037
const { name: table, schema } = (await RunQuery(req.headers.pg, getTableQuery)).data[0]
4138

42-
const query = addColumnSqlize({ schema, table, name, type })
39+
const addColumnArgs = req.body
40+
delete addColumnArgs.tableId
41+
addColumnArgs.table = table
42+
addColumnArgs.schema = schema
43+
const query = addColumnSqlize(addColumnArgs)
4344
await RunQuery(req.headers.pg, query)
4445

4546
const getColumnQuery = getColumnSqlize(tableId, name)
@@ -59,12 +60,11 @@ router.patch('/:id', async (req, res) => {
5960
const column = (await RunQuery(req.headers.pg, getColumnQuery)).data[0]
6061
const { schema, table, name: oldName } = column
6162

62-
const { name, type } = req.body as {
63-
name?: string
64-
type?: string
65-
}
66-
67-
const query = patchColumnSqlize({ schema, table, oldName, name, type })
63+
const alterColumnArgs = req.body
64+
alterColumnArgs.schema = schema
65+
alterColumnArgs.table = table
66+
alterColumnArgs.oldName = oldName
67+
const query = alterColumnSqlize(alterColumnArgs)
6868
await RunQuery(req.headers.pg, query)
6969

7070
const updated = (await RunQuery(req.headers.pg, getColumnQuery)).data[0]
@@ -100,13 +100,35 @@ const addColumnSqlize = ({
100100
table,
101101
name,
102102
type,
103+
defaultValue,
104+
isIdentity = false,
105+
isNullable = true,
106+
isPrimaryKey = false,
107+
isUnique = false,
103108
}: {
104109
schema: string
105110
table: string
106111
name: string
107112
type: string
113+
defaultValue?: any
114+
isIdentity?: boolean
115+
isNullable?: boolean
116+
isPrimaryKey?: boolean
117+
isUnique?: boolean
108118
}) => {
109-
return `ALTER TABLE "${schema}"."${table}" ADD COLUMN "${name}" "${type}"`
119+
const defaultValueSql = defaultValue === undefined ? '' : `DEFAULT ${defaultValue}`
120+
const isIdentitySql = isIdentity ? 'GENERATED BY DEFAULT AS IDENTITY' : ''
121+
const isNullableSql = isNullable ? 'NULL' : 'NOT NULL'
122+
const isPrimaryKeySql = isPrimaryKey ? 'PRIMARY KEY' : ''
123+
const isUniqueSql = isUnique ? 'UNIQUE' : ''
124+
125+
return `
126+
ALTER TABLE "${schema}"."${table}" ADD COLUMN "${name}" "${type}"
127+
${defaultValueSql}
128+
${isIdentitySql}
129+
${isNullableSql}
130+
${isPrimaryKeySql}
131+
${isUniqueSql}`
110132
}
111133
const getColumnSqlize = (tableId: number, name: string) => {
112134
return SQL``.append(columns).append(SQL` WHERE c.oid = ${tableId} AND column_name = ${name}`)
@@ -116,18 +138,24 @@ const getColumnByPosSqlize = (tableId: number, ordinalPos: number) => {
116138
.append(columns)
117139
.append(SQL` WHERE c.oid = ${tableId} AND ordinal_position = ${ordinalPos}`)
118140
}
119-
const patchColumnSqlize = ({
141+
const alterColumnSqlize = ({
120142
schema,
121143
table,
122144
oldName,
123145
name,
124146
type,
147+
dropDefault = false,
148+
defaultValue,
149+
isNullable,
125150
}: {
126151
schema: string
127152
table: string
128153
oldName: string
129154
name?: string
130155
type?: string
156+
dropDefault?: boolean
157+
defaultValue?: any
158+
isNullable?: boolean
131159
}) => {
132160
const nameSql =
133161
name === undefined
@@ -137,9 +165,24 @@ const patchColumnSqlize = ({
137165
type === undefined
138166
? ''
139167
: `ALTER TABLE "${schema}"."${table}" ALTER COLUMN "${oldName}" SET DATA TYPE "${type}";`
140-
// Make sure typeSql comes first
168+
let defaultValueSql = ''
169+
if (dropDefault) {
170+
defaultValueSql = `ALTER TABLE "${schema}"."${table}" ALTER COLUMN "${oldName}" DROP DEFAULT;`
171+
} else if (defaultValue !== undefined) {
172+
defaultValueSql = `ALTER TABLE "${schema}"."${table}" ALTER COLUMN "${oldName}" SET DEFAULT ${defaultValue};`
173+
}
174+
let isNullableSql = ''
175+
if (isNullable !== undefined) {
176+
isNullableSql = isNullable
177+
? `ALTER TABLE "${schema}"."${table}" ALTER COLUMN "${oldName}" DROP NOT NULL;`
178+
: `ALTER TABLE "${schema}"."${table}" ALTER COLUMN "${oldName}" SET NOT NULL;`
179+
}
180+
181+
// nameSql must be last.
141182
return `
142183
BEGIN;
184+
${isNullableSql}
185+
${defaultValueSql}
143186
${typeSql}
144187
${nameSql}
145188
COMMIT;`

test/integration/index.spec.js

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,30 +260,51 @@ describe('/tables', async () => {
260260
})
261261
it('POST /columns', async () => {
262262
const { data: newTable } = await axios.post(`${URL}/tables`, { name: 'foo bar' })
263-
await axios.post(`${URL}/columns`, { tableId: newTable.id, name: 'foo bar', type: 'int2' })
263+
await axios.post(`${URL}/columns`, {
264+
tableId: newTable.id,
265+
name: 'foo bar',
266+
type: 'int2',
267+
defaultValue: 42,
268+
isNullable: false,
269+
// Currently no way to test these:
270+
// isPrimaryKey: true,
271+
// isUnique: true,
272+
})
264273

265274
const { data: columns } = await axios.get(`${URL}/columns`)
266-
const newColumnExists = columns.some(
275+
const newColumn = columns.find(
267276
(column) =>
268277
column.id === `${newTable.id}.1` && column.name === 'foo bar' && column.format === 'int2'
269278
)
270-
assert.equal(newColumnExists, true)
279+
assert.equal(newColumn.default_value, 42)
280+
assert.equal(newColumn.is_nullable, false)
271281

272282
await axios.delete(`${URL}/columns/${newTable.id}.1`)
273283
await axios.delete(`${URL}/tables/${newTable.id}`)
274284
})
275285
it('PATCH /columns', async () => {
276286
const { data: newTable } = await axios.post(`${URL}/tables`, { name: 'foo bar' })
277-
await axios.post(`${URL}/columns`, { tableId: newTable.id, name: 'foo', type: 'int2' })
287+
await axios.post(`${URL}/columns`, {
288+
tableId: newTable.id,
289+
name: 'foo',
290+
type: 'int2',
291+
defaultValue: 42,
292+
})
278293

279-
await axios.patch(`${URL}/columns/${newTable.id}.1`, { name: 'foo bar', type: 'int4' })
294+
await axios.patch(`${URL}/columns/${newTable.id}.1`, {
295+
name: 'foo bar',
296+
type: 'int4',
297+
dropDefault: true,
298+
isNullable: false,
299+
})
280300

281301
const { data: columns } = await axios.get(`${URL}/columns`)
282-
const updatedColumnExists = columns.some(
302+
const updatedColumn = columns.find(
283303
(column) =>
284304
column.id === `${newTable.id}.1` && column.name === 'foo bar' && column.format === 'int4'
285305
)
286-
assert.equal(updatedColumnExists, true)
306+
assert.equal(updatedColumn.default_value, null)
307+
assert.equal(updatedColumn.is_nullable, false)
287308

288309
await axios.delete(`${URL}/columns/${newTable.id}.1`)
289310
await axios.delete(`${URL}/tables/${newTable.id}`)

0 commit comments

Comments
 (0)