Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions drizzle-kit/src/serializer/pgSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ const column = object({
uniqueName: string().optional(),
nullsNotDistinct: boolean().optional(),
generated: object({
type: literal('stored'),
type: enumType(['virtual', 'stored']),
as: string(),
}).optional(),
identity: sequenceSchema
Expand All @@ -207,7 +207,7 @@ const columnSquashed = object({
uniqueName: string().optional(),
nullsNotDistinct: boolean().optional(),
generated: object({
type: literal('stored'),
type: enumType(['virtual', 'stored']),
as: string(),
}).optional(),
identity: string().optional(),
Expand Down
4 changes: 2 additions & 2 deletions drizzle-kit/src/serializer/pgSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export const generatePgSnapshot = (
: typeof generated.as === 'function'
? dialect.sqlToQuery(generated.as() as SQL).sql
: (generated.as as any),
type: 'stored',
type: generated.mode ?? 'stored',
}
: undefined,
identity: identity
Expand Down Expand Up @@ -780,7 +780,7 @@ export const generatePgSnapshot = (
: typeof generated.as === 'function'
? dialect.sqlToQuery(generated.as() as SQL).sql
: (generated.as as any),
type: 'stored',
type: generated.mode ?? 'stored',
}
: undefined,
identity: identity
Expand Down
8 changes: 6 additions & 2 deletions drizzle-kit/src/sqlgenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ class PgCreateTableConvertor extends Convertor {
const type = parseType(schemaPrefix, column.type);
const generated = column.generated;

const generatedStatement = generated ? ` GENERATED ALWAYS AS (${generated?.as}) STORED` : '';
const generatedStatement = generated
? ` GENERATED ALWAYS AS (${generated?.as}) ${generated?.type.toUpperCase()}`
: '';

const unsquashedIdentity = column.identity
? PgSquasher.unsquashIdentity(column.identity)
Expand Down Expand Up @@ -1793,7 +1795,9 @@ class PgAlterTableAddColumnConvertor extends Convertor {
})`
: '';

const generatedStatement = generated ? ` GENERATED ALWAYS AS (${generated?.as}) STORED` : '';
const generatedStatement = generated
? ` GENERATED ALWAYS AS (${generated?.as}) ${generated?.type.toUpperCase()}`
: '';

return `ALTER TABLE ${tableNameWithSchema} ADD COLUMN "${name}" ${fixedType}${primaryKeyStatement}${defaultStatement}${generatedStatement}${notNullStatement}${identityStatement};`;
}
Expand Down
226 changes: 226 additions & 0 deletions drizzle-kit/tests/pg-generated.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,229 @@ test('generated as string: change generated constraint', async () => {
'ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS ("users"."name" || \'hello\') STORED;',
]);
});

test('generated as callback: add column with virtual generated constraint', async () => {
const from = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
}),
};
const to = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
(): SQL => sql`${to.users.name} || 'hello'`,
{ mode: 'virtual' },
),
}),
};

const { statements, sqlStatements } = await diffTestSchemas(from, to, []);

expect(statements).toStrictEqual([
{
column: {
generated: {
as: '"users"."name" || \'hello\'',
type: 'virtual',
},
name: 'gen_name',
notNull: false,
primaryKey: false,
type: 'text',
},
schema: '',
tableName: 'users',
type: 'alter_table_add_column',
},
]);
expect(sqlStatements).toStrictEqual([
`ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS (\"users\".\"name\" || 'hello') VIRTUAL;`,
]);
});

test('generated as SQL: add column with virtual generated constraint', async () => {
const from = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
}),
};
const to = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
sql`concat("users"."name", 'hello')`,
{ mode: 'virtual' },
),
}),
};

const { statements, sqlStatements } = await diffTestSchemas(from, to, []);

expect(statements).toStrictEqual([
{
column: {
generated: {
as: 'concat("users"."name", \'hello\')',
type: 'virtual',
},
name: 'gen_name',
notNull: false,
primaryKey: false,
type: 'text',
},
schema: '',
tableName: 'users',
type: 'alter_table_add_column',
},
]);
expect(sqlStatements).toStrictEqual([
`ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS (concat("users"."name", 'hello')) VIRTUAL;`,
]);
});

test('generated as string: add column with virtual generated constraint', async () => {
const from = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
}),
};
const to = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
`\"users\".\"name\" || 'hello'`,
{ mode: 'virtual' },
),
}),
};

const { statements, sqlStatements } = await diffTestSchemas(from, to, []);

expect(statements).toStrictEqual([
{
column: {
generated: {
as: '"users"."name" || \'hello\'',
type: 'virtual',
},
name: 'gen_name',
notNull: false,
primaryKey: false,
type: 'text',
},
schema: '',
tableName: 'users',
type: 'alter_table_add_column',
},
]);
expect(sqlStatements).toStrictEqual([
`ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS ("users"."name" || 'hello') VIRTUAL;`,
]);
});

test('change from stored to virtual generated constraint', async () => {
const from = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
sql`"users"."name" || 'hello'`,
{ mode: 'stored' },
),
}),
};
const to = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
sql`"users"."name" || 'hello'`,
{ mode: 'virtual' },
),
}),
};

const { statements, sqlStatements } = await diffTestSchemas(from, to, []);

expect(statements).toStrictEqual([
{
columnAutoIncrement: undefined,
columnDefault: undefined,
columnGenerated: { as: '"users"."name" || \'hello\'', type: 'virtual' },
columnName: 'gen_name',
columnNotNull: false,
columnOnUpdate: undefined,
columnPk: false,
newDataType: 'text',
schema: '',
tableName: 'users',
type: 'alter_table_alter_column_alter_generated',
},
]);
expect(sqlStatements).toStrictEqual([
'ALTER TABLE "users" drop column "gen_name";',
'ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS ("users"."name" || \'hello\') VIRTUAL;',
]);
});

test('change from virtual to stored generated constraint', async () => {
const from = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
sql`"users"."name" || 'hello'`,
{ mode: 'virtual' },
),
}),
};
const to = {
users: pgTable('users', {
id: integer('id'),
id2: integer('id2'),
name: text('name'),
generatedName: text('gen_name').generatedAlwaysAs(
sql`"users"."name" || 'hello'`,
{ mode: 'stored' },
),
}),
};

const { statements, sqlStatements } = await diffTestSchemas(from, to, []);

expect(statements).toStrictEqual([
{
columnAutoIncrement: undefined,
columnDefault: undefined,
columnGenerated: { as: '"users"."name" || \'hello\'', type: 'stored' },
columnName: 'gen_name',
columnNotNull: false,
columnOnUpdate: undefined,
columnPk: false,
newDataType: 'text',
schema: '',
tableName: 'users',
type: 'alter_table_alter_column_alter_generated',
},
]);
expect(sqlStatements).toStrictEqual([
'ALTER TABLE "users" drop column "gen_name";',
'ALTER TABLE "users" ADD COLUMN "gen_name" text GENERATED ALWAYS AS ("users"."name" || \'hello\') STORED;',
]);
});
8 changes: 6 additions & 2 deletions drizzle-orm/src/pg-core/columns/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export interface ReferenceConfig {
};
}

export interface PgGeneratedColumnConfig {
mode?: 'virtual' | 'stored';
}

export interface PgColumnBuilderBase<
T extends ColumnBuilderBaseConfig<ColumnDataType, string> = ColumnBuilderBaseConfig<ColumnDataType, string>,
TTypeConfig extends object = object,
Expand Down Expand Up @@ -83,13 +87,13 @@ export abstract class PgColumnBuilder<
return this;
}

generatedAlwaysAs(as: SQL | T['data'] | (() => SQL)): HasGenerated<this, {
generatedAlwaysAs(as: SQL | T['data'] | (() => SQL), config?: PgGeneratedColumnConfig): HasGenerated<this, {
type: 'always';
}> {
this.config.generated = {
as,
type: 'always',
mode: 'stored',
mode: config?.mode ?? 'stored',
};
return this as HasGenerated<this, {
type: 'always';
Expand Down
Loading