Skip to content

Commit 74ac110

Browse files
committed
feat: add support for batch column add
1 parent 3514106 commit 74ac110

File tree

5 files changed

+478
-241
lines changed

5 files changed

+478
-241
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ typings/
5151
# Optional npm cache directory
5252
.npm
5353

54+
# npm config
55+
.npmrc
56+
5457
# Optional eslint cache
5558
.eslintcache
5659

src/lib/PostgresMetaColumns.ts

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -115,89 +115,101 @@ WHERE
115115
}
116116
}
117117

118-
async create({
119-
table_id,
120-
name,
121-
type,
122-
default_value,
123-
default_value_format = 'literal',
124-
is_identity = false,
125-
identity_generation = 'BY DEFAULT',
126-
// Can't pick a value as default since regular columns are nullable by default but PK columns aren't
127-
is_nullable,
128-
is_primary_key = false,
129-
is_unique = false,
130-
comment,
131-
check,
132-
}: {
133-
table_id: number
134-
name: string
135-
type: string
136-
default_value?: any
137-
default_value_format?: 'expression' | 'literal'
138-
is_identity?: boolean
139-
identity_generation?: 'BY DEFAULT' | 'ALWAYS'
140-
is_nullable?: boolean
141-
is_primary_key?: boolean
142-
is_unique?: boolean
143-
comment?: string
144-
check?: string
145-
}): Promise<PostgresMetaResult<PostgresColumn>> {
118+
async create(
119+
columns: {
120+
table_id: number
121+
name: string
122+
type: string
123+
default_value?: any
124+
default_value_format?: 'expression' | 'literal'
125+
is_identity?: boolean
126+
identity_generation?: 'BY DEFAULT' | 'ALWAYS'
127+
is_nullable?: boolean
128+
is_primary_key?: boolean
129+
is_unique?: boolean
130+
comment?: string
131+
check?: string
132+
}[]
133+
): Promise<PostgresMetaResult<PostgresColumn[]>> {
134+
const { table_id } = columns[0]
135+
146136
const { data, error } = await this.metaTables.retrieve({ id: table_id })
147137
if (error) {
148138
return { data: null, error }
149139
}
150140
const { name: table, schema } = data!
141+
let sql = `BEGIN;`
142+
for (const column of columns) {
143+
const {
144+
name,
145+
type,
146+
default_value,
147+
default_value_format = 'literal',
148+
is_identity = false,
149+
identity_generation = 'BY DEFAULT',
150+
// Can't pick a value as default since regular columns are nullable by default but PK columns aren't
151+
is_nullable,
152+
is_primary_key = false,
153+
is_unique = false,
154+
comment,
155+
check,
156+
} = column
157+
sql += `
158+
ALTER TABLE ${ident(schema)}.${ident(table)} ADD COLUMN ${ident(name)} ${typeIdent(type)}`
151159

152-
let defaultValueClause = ''
153-
if (is_identity) {
154-
if (default_value !== undefined) {
155-
return {
156-
data: null,
157-
error: { message: 'Columns cannot both be identity and have a default value' },
160+
let defaultValueClause = ''
161+
if (is_identity) {
162+
if (default_value !== undefined) {
163+
return {
164+
data: null,
165+
error: { message: 'Columns cannot both be identity and have a default value' },
166+
}
158167
}
159-
}
160168

161-
defaultValueClause = `GENERATED ${identity_generation} AS IDENTITY`
162-
} else {
163-
if (default_value === undefined) {
164-
// skip
165-
} else if (default_value_format === 'expression') {
166-
defaultValueClause = `DEFAULT ${default_value}`
169+
defaultValueClause = `GENERATED ${identity_generation} AS IDENTITY`
167170
} else {
168-
defaultValueClause = `DEFAULT ${literal(default_value)}`
171+
if (default_value === undefined) {
172+
// skip
173+
} else if (default_value_format === 'expression') {
174+
defaultValueClause = `DEFAULT ${default_value}`
175+
} else {
176+
defaultValueClause = `DEFAULT ${literal(default_value)}`
177+
}
169178
}
170-
}
171179

172-
let isNullableClause = ''
173-
if (is_nullable !== undefined) {
174-
isNullableClause = is_nullable ? 'NULL' : 'NOT NULL'
180+
let isNullableClause = ''
181+
if (is_nullable !== undefined) {
182+
isNullableClause = is_nullable ? 'NULL' : 'NOT NULL'
183+
}
184+
const isPrimaryKeyClause = is_primary_key ? 'PRIMARY KEY' : ''
185+
const isUniqueClause = is_unique ? 'UNIQUE' : ''
186+
const checkSql = check === undefined ? '' : `CHECK (${check})`
187+
const commentSql =
188+
comment === undefined
189+
? ''
190+
: `COMMENT ON COLUMN ${ident(schema)}.${ident(table)}.${ident(name)} IS ${literal(
191+
comment
192+
)}`
193+
194+
sql += `
195+
${defaultValueClause}
196+
${isNullableClause}
197+
${isPrimaryKeyClause}
198+
${isUniqueClause}
199+
${checkSql};
200+
${commentSql};`
175201
}
176-
const isPrimaryKeyClause = is_primary_key ? 'PRIMARY KEY' : ''
177-
const isUniqueClause = is_unique ? 'UNIQUE' : ''
178-
const checkSql = check === undefined ? '' : `CHECK (${check})`
179-
const commentSql =
180-
comment === undefined
181-
? ''
182-
: `COMMENT ON COLUMN ${ident(schema)}.${ident(table)}.${ident(name)} IS ${literal(comment)}`
183202

184-
const sql = `
185-
BEGIN;
186-
ALTER TABLE ${ident(schema)}.${ident(table)} ADD COLUMN ${ident(name)} ${typeIdent(type)}
187-
${defaultValueClause}
188-
${isNullableClause}
189-
${isPrimaryKeyClause}
190-
${isUniqueClause}
191-
${checkSql};
192-
${commentSql};
193-
COMMIT;`
203+
sql += `COMMIT;`
194204
{
195205
const { error } = await this.query(sql)
196206
if (error) {
197207
return { data: null, error }
198208
}
199209
}
200-
return await this.retrieve({ name, table, schema })
210+
const res = await this.list({ tableId: table_id, includedSchemas: [schema] })
211+
res.data = res.data?.filter((d) => columns.find((c) => d.name === c.name)) as PostgresColumn[]
212+
return res
201213
}
202214

203215
async update(

src/server/routes/columns.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => {
140140
headers: Type.Object({
141141
pg: Type.String(),
142142
}),
143-
body: postgresColumnCreateSchema,
143+
body: Type.Union([postgresColumnCreateSchema, Type.Array(postgresColumnCreateSchema)]),
144144
response: {
145-
200: postgresColumnSchema,
145+
200: Type.Union([postgresColumnSchema, Type.Array(postgresColumnSchema)]),
146146
400: Type.Object({
147147
error: Type.String(),
148148
}),
@@ -153,7 +153,8 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => {
153153
const connectionString = request.headers.pg
154154

155155
const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString })
156-
const { data, error } = await pgMeta.columns.create(request.body)
156+
const colMutations = Array.isArray(request.body) ? request.body : [request.body]
157+
const { data, error } = await pgMeta.columns.create(colMutations)
157158
await pgMeta.end()
158159
if (error) {
159160
request.log.error({ error, request: extractRequestForLogging(request) })
@@ -162,7 +163,7 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => {
162163
return { error: error.message }
163164
}
164165

165-
return data
166+
return Array.isArray(request.body) ? data : data[0]
166167
}
167168
)
168169

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy