mirror of
https://github.com/knex/knex.git
synced 2025-12-28 23:48:58 +00:00
Add check to only create native enum once (#3658)
Co-authored-by: Morgan Zolob <morgan.zolob@ingrooves.com>
This commit is contained in:
parent
c074ec7b9d
commit
92907e80e2
@ -60,6 +60,9 @@ class TableCompiler_PG extends TableCompiler {
|
||||
col.columnBuilder.queryContext()
|
||||
);
|
||||
|
||||
// To alter enum columns they must be cast to text first
|
||||
const isEnum = col.type === 'enu';
|
||||
|
||||
this.pushQuery({
|
||||
sql: `alter table ${quotedTableName} alter column ${colName} drop default`,
|
||||
bindings: [],
|
||||
@ -69,7 +72,9 @@ class TableCompiler_PG extends TableCompiler {
|
||||
bindings: [],
|
||||
});
|
||||
this.pushQuery({
|
||||
sql: `alter table ${quotedTableName} alter column ${colName} type ${type} using (${colName}::${type})`,
|
||||
sql: `alter table ${quotedTableName} alter column ${colName} type ${type} using (${colName}${
|
||||
isEnum ? '::text::' : '::'
|
||||
}${type})`,
|
||||
bindings: [],
|
||||
});
|
||||
|
||||
|
||||
@ -64,10 +64,14 @@ class ColumnCompiler {
|
||||
}
|
||||
|
||||
getColumnType() {
|
||||
const type = this[this.type];
|
||||
return typeof type === 'function'
|
||||
? type.apply(this, tail(this.args))
|
||||
: type;
|
||||
// Column type is cached so side effects (such as in pg native enums) are only run once
|
||||
if (!this._columnType) {
|
||||
const type = this[this.type];
|
||||
this._columnType =
|
||||
typeof type === 'function' ? type.apply(this, tail(this.args)) : type;
|
||||
}
|
||||
|
||||
return this._columnType;
|
||||
}
|
||||
|
||||
getModifiers() {
|
||||
|
||||
107
test/integration2/schema/enum-native.spec.js
Normal file
107
test/integration2/schema/enum-native.spec.js
Normal file
@ -0,0 +1,107 @@
|
||||
const { expect } = require('chai');
|
||||
const {
|
||||
Db,
|
||||
getAllDbs,
|
||||
getKnexForDb,
|
||||
} = require('../util/knex-instance-provider');
|
||||
|
||||
describe('Schema', () => {
|
||||
describe('native enum columns', () => {
|
||||
getAllDbs()
|
||||
// enum useNative is only supported by Postgres
|
||||
.filter((db) => db === Db.PostgresSQL)
|
||||
.forEach((db) => {
|
||||
describe(db, () => {
|
||||
let knex;
|
||||
const tblName = 'table_with_native_enum';
|
||||
const enumColumns = [
|
||||
{
|
||||
column: 'enum_col1',
|
||||
values: ['foo', 'bar'],
|
||||
typeName: 'test_enum1',
|
||||
},
|
||||
{
|
||||
column: 'enum_col2',
|
||||
values: ['baz', 'qux'],
|
||||
typeName: 'test_enum2',
|
||||
},
|
||||
];
|
||||
|
||||
before(async () => {
|
||||
knex = getKnexForDb(db);
|
||||
await knex.schema.dropTableIfExists(tblName);
|
||||
|
||||
for (const enumColumn of enumColumns) {
|
||||
await knex.raw(`DROP TYPE IF EXISTS ${enumColumn.typeName}`);
|
||||
}
|
||||
|
||||
await knex.schema.createTable(tblName, (table) => {
|
||||
for (const enumColumn of enumColumns) {
|
||||
table.enum(enumColumn.column, enumColumn.values, {
|
||||
useNative: true,
|
||||
enumName: enumColumn.typeName,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await knex.schema.dropTable(tblName);
|
||||
|
||||
for (const enumColumn of enumColumns) {
|
||||
await knex.raw(`DROP TYPE ${enumColumn.typeName}`);
|
||||
}
|
||||
|
||||
return knex.destroy();
|
||||
});
|
||||
|
||||
it('Creates native enums', async () => {
|
||||
for (const enumColumn of enumColumns) {
|
||||
const res = await knex
|
||||
.select('data_type', 'udt_name')
|
||||
.from('information_schema.columns')
|
||||
.where({ table_name: tblName, column_name: enumColumn.column });
|
||||
|
||||
expect(res[0].data_type).to.equal('USER-DEFINED');
|
||||
expect(res[0].udt_name).to.equal(enumColumn.typeName);
|
||||
}
|
||||
});
|
||||
|
||||
describe('Altering', async () => {
|
||||
const enumColumn = enumColumns[0].column;
|
||||
const enumTypeName = 'alter_test_enum';
|
||||
|
||||
before(async () => {
|
||||
await knex.raw(`DROP TYPE IF EXISTS ${enumTypeName}`);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await knex.schema.alterTable(tblName, (table) => {
|
||||
table.dropColumn(enumColumn);
|
||||
});
|
||||
await knex.raw(`DROP TYPE ${enumTypeName}`);
|
||||
});
|
||||
|
||||
it('Allows altering native enums', async () => {
|
||||
await knex.schema.alterTable(tblName, (table) => {
|
||||
table
|
||||
.enum(enumColumn, ['altered', 'values'], {
|
||||
useNative: true,
|
||||
enumName: enumTypeName,
|
||||
})
|
||||
.alter();
|
||||
});
|
||||
|
||||
const res = await knex
|
||||
.select('data_type', 'udt_name')
|
||||
.from('information_schema.columns')
|
||||
.where({ table_name: tblName, column_name: enumColumn });
|
||||
|
||||
expect(res[0].data_type).to.equal('USER-DEFINED');
|
||||
expect(res[0].udt_name).to.equal(enumTypeName);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -998,6 +998,85 @@ describe('PostgreSQL SchemaBuilder', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('adding enum with useNative and alter', function () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function (table) {
|
||||
table
|
||||
.enu('foo', ['bar', 'baz'], {
|
||||
useNative: true,
|
||||
enumName: 'foo_type',
|
||||
})
|
||||
.notNullable()
|
||||
.alter();
|
||||
})
|
||||
.toSQL();
|
||||
equal(5, tableSql.length);
|
||||
|
||||
const expectedSql = [
|
||||
"create type \"foo_type\" as enum ('bar', 'baz')",
|
||||
'alter table "users" alter column "foo" drop default',
|
||||
'alter table "users" alter column "foo" drop not null',
|
||||
'alter table "users" alter column "foo" type "foo_type" using ("foo"::text::"foo_type")',
|
||||
'alter table "users" alter column "foo" set not null',
|
||||
];
|
||||
|
||||
for (let i = 0; i < tableSql.length; i++) {
|
||||
expect(tableSql[i].sql).to.equal(expectedSql[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('adding multiple useNative enums with some alters', function () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function (table) {
|
||||
table
|
||||
.enu('foo', ['bar', 'baz'], {
|
||||
useNative: true,
|
||||
enumName: 'foo_type',
|
||||
})
|
||||
.notNullable()
|
||||
.alter();
|
||||
|
||||
table.enu('bar', ['foo', 'baz'], {
|
||||
useNative: true,
|
||||
enumName: 'bar_type',
|
||||
});
|
||||
|
||||
table
|
||||
.enu('baz', ['foo', 'bar'], {
|
||||
useNative: true,
|
||||
enumName: 'baz_type',
|
||||
})
|
||||
.defaultTo('foo')
|
||||
.alter();
|
||||
})
|
||||
.toSQL();
|
||||
equal(12, tableSql.length);
|
||||
|
||||
const expectedSql = [
|
||||
"create type \"baz_type\" as enum ('foo', 'bar')",
|
||||
"create type \"foo_type\" as enum ('bar', 'baz')",
|
||||
"create type \"bar_type\" as enum ('foo', 'baz')",
|
||||
|
||||
'alter table "users" add column "bar" "bar_type"',
|
||||
|
||||
'alter table "users" alter column "foo" drop default',
|
||||
'alter table "users" alter column "foo" drop not null',
|
||||
'alter table "users" alter column "foo" type "foo_type" using ("foo"::text::"foo_type")',
|
||||
'alter table "users" alter column "foo" set not null',
|
||||
|
||||
'alter table "users" alter column "baz" drop default',
|
||||
'alter table "users" alter column "baz" drop not null',
|
||||
'alter table "users" alter column "baz" type "baz_type" using ("baz"::text::"baz_type")',
|
||||
'alter table "users" alter column "baz" set default \'foo\'',
|
||||
];
|
||||
|
||||
for (let i = 0; i < tableSql.length; i++) {
|
||||
expect(tableSql[i].sql).to.equal(expectedSql[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('adding date', function () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user