diff --git a/.gitignore b/.gitignore index 8d050113..250c4f26 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,9 @@ typings/ # Optional npm cache directory .npm +# npm config +.npmrc + # Optional eslint cache .eslintcache diff --git a/src/lib/PostgresMetaColumns.ts b/src/lib/PostgresMetaColumns.ts index 07224c9c..250d7904 100644 --- a/src/lib/PostgresMetaColumns.ts +++ b/src/lib/PostgresMetaColumns.ts @@ -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> { + 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> { + 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( diff --git a/src/server/routes/columns.ts b/src/server/routes/columns.ts index 54a7ba2a..d640b298 100644 --- a/src/server/routes/columns.ts +++ b/src/server/routes/columns.ts @@ -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(), }), @@ -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) }) @@ -162,7 +163,7 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => { return { error: error.message } } - return data + return Array.isArray(request.body) ? data : data[0] } )