Skip to content

Commit

Permalink
feat: add support for batch column add
Browse files Browse the repository at this point in the history
  • Loading branch information
mahendraHegde committed Sep 7, 2023
1 parent 3514106 commit a394a18
Show file tree
Hide file tree
Showing 5 changed files with 499 additions and 241 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ typings/
# Optional npm cache directory
.npm

# npm config
.npmrc

# Optional eslint cache
.eslintcache

Expand Down
140 changes: 76 additions & 64 deletions src/lib/PostgresMetaColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,89 +115,101 @@ WHERE
}
}

async create({
table_id,
name,
type,
default_value,
default_value_format = 'literal',
is_identity = false,
identity_generation = 'BY DEFAULT',
// Can't pick a value as default since regular columns are nullable by default but PK columns aren't
is_nullable,
is_primary_key = false,
is_unique = false,
comment,
check,
}: {
table_id: number
name: string
type: string
default_value?: any
default_value_format?: 'expression' | 'literal'
is_identity?: boolean
identity_generation?: 'BY DEFAULT' | 'ALWAYS'
is_nullable?: boolean
is_primary_key?: boolean
is_unique?: boolean
comment?: string
check?: string
}): Promise<PostgresMetaResult<PostgresColumn>> {
async create(
columns: {
table_id: number
name: string
type: string
default_value?: any
default_value_format?: 'expression' | 'literal'
is_identity?: boolean
identity_generation?: 'BY DEFAULT' | 'ALWAYS'
is_nullable?: boolean
is_primary_key?: boolean
is_unique?: boolean
comment?: string
check?: string
}[]
): Promise<PostgresMetaResult<PostgresColumn[]>> {
const { table_id } = columns[0]

const { data, error } = await this.metaTables.retrieve({ id: table_id })
if (error) {
return { data: null, error }
}
const { name: table, schema } = data!
let sql = `BEGIN;`
for (const column of columns) {
const {
name,
type,
default_value,
default_value_format = 'literal',
is_identity = false,
identity_generation = 'BY DEFAULT',
// Can't pick a value as default since regular columns are nullable by default but PK columns aren't
is_nullable,
is_primary_key = false,
is_unique = false,
comment,
check,
} = column
sql += `
ALTER TABLE ${ident(schema)}.${ident(table)} ADD COLUMN ${ident(name)} ${typeIdent(type)}`

let defaultValueClause = ''
if (is_identity) {
if (default_value !== undefined) {
return {
data: null,
error: { message: 'Columns cannot both be identity and have a default value' },
let defaultValueClause = ''
if (is_identity) {
if (default_value !== undefined) {
return {
data: null,
error: { message: 'Columns cannot both be identity and have a default value' },
}
}
}

defaultValueClause = `GENERATED ${identity_generation} AS IDENTITY`
} else {
if (default_value === undefined) {
// skip
} else if (default_value_format === 'expression') {
defaultValueClause = `DEFAULT ${default_value}`
defaultValueClause = `GENERATED ${identity_generation} AS IDENTITY`
} else {
defaultValueClause = `DEFAULT ${literal(default_value)}`
if (default_value === undefined) {
// skip
} else if (default_value_format === 'expression') {
defaultValueClause = `DEFAULT ${default_value}`
} else {
defaultValueClause = `DEFAULT ${literal(default_value)}`
}
}
}

let isNullableClause = ''
if (is_nullable !== undefined) {
isNullableClause = is_nullable ? 'NULL' : 'NOT NULL'
let isNullableClause = ''
if (is_nullable !== undefined) {
isNullableClause = is_nullable ? 'NULL' : 'NOT NULL'
}
const isPrimaryKeyClause = is_primary_key ? 'PRIMARY KEY' : ''
const isUniqueClause = is_unique ? 'UNIQUE' : ''
const checkSql = check === undefined ? '' : `CHECK (${check})`
const commentSql =
comment === undefined
? ''
: `COMMENT ON COLUMN ${ident(schema)}.${ident(table)}.${ident(name)} IS ${literal(
comment
)}`

sql += `
${defaultValueClause}
${isNullableClause}
${isPrimaryKeyClause}
${isUniqueClause}
${checkSql};
${commentSql};`
}
const isPrimaryKeyClause = is_primary_key ? 'PRIMARY KEY' : ''
const isUniqueClause = is_unique ? 'UNIQUE' : ''
const checkSql = check === undefined ? '' : `CHECK (${check})`
const commentSql =
comment === undefined
? ''
: `COMMENT ON COLUMN ${ident(schema)}.${ident(table)}.${ident(name)} IS ${literal(comment)}`

const sql = `
BEGIN;
ALTER TABLE ${ident(schema)}.${ident(table)} ADD COLUMN ${ident(name)} ${typeIdent(type)}
${defaultValueClause}
${isNullableClause}
${isPrimaryKeyClause}
${isUniqueClause}
${checkSql};
${commentSql};
COMMIT;`
sql += `COMMIT;`
{
const { error } = await this.query(sql)
if (error) {
return { data: null, error }
}
}
return await this.retrieve({ name, table, schema })
const res = await this.list({ tableId: table_id, includedSchemas: [schema] })
res.data = res.data?.filter((d) => columns.find((c) => d.name === c.name)) as PostgresColumn[]
return res
}

async update(
Expand Down
9 changes: 5 additions & 4 deletions src/server/routes/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => {
headers: Type.Object({
pg: Type.String(),
}),
body: postgresColumnCreateSchema,
body: Type.Union([postgresColumnCreateSchema, Type.Array(postgresColumnCreateSchema)]),
response: {
200: postgresColumnSchema,
200: Type.Union([postgresColumnSchema, Type.Array(postgresColumnSchema)]),
400: Type.Object({
error: Type.String(),
}),
Expand All @@ -153,7 +153,8 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => {
const connectionString = request.headers.pg

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

return data
return Array.isArray(request.body) ? data : data[0]
}
)

Expand Down

0 comments on commit a394a18

Please sign in to comment.