Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for native enums on Postgres #2632

Merged
merged 1 commit into from May 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/dialects/postgres/schema/columncompiler.js
Expand Up @@ -30,8 +30,17 @@ assign(ColumnCompiler_PG.prototype, {

// Create the column definition for an enum type.
// Using method "2" here: http://stackoverflow.com/a/10984951/525714
enu(allowed) {
return `text check (${this.formatter.wrap(this.args[0])} in ('${allowed.join("', '")}'))`;
enu(allowed, options) {
options = options || {};

const values = allowed.join("', '");

if (options.useNative) {
this.tableCompiler.unshiftQuery(`create type "${options.enumName}" as enum ('${values}')`);

return `"${options.enumName}"`;
}
return `text check (${this.formatter.wrap(this.args[0])} in ('${values}'))`;
},

double: 'double precision',
Expand Down
2 changes: 2 additions & 0 deletions src/schema/columncompiler.js
Expand Up @@ -25,6 +25,8 @@ ColumnCompiler.prototype.pushQuery = helpers.pushQuery

ColumnCompiler.prototype.pushAdditional = helpers.pushAdditional

ColumnCompiler.prototype.unshiftQuery = helpers.unshiftQuery

ColumnCompiler.prototype._defaultMap = {
'columnName': function() {
if (!this.isIncrements) {
Expand Down
4 changes: 3 additions & 1 deletion src/schema/compiler.js
@@ -1,5 +1,5 @@

import { pushQuery, pushAdditional } from './helpers';
import { pushQuery, pushAdditional, unshiftQuery } from './helpers';

import { assign, isUndefined } from 'lodash'

Expand All @@ -20,6 +20,8 @@ assign(SchemaCompiler.prototype, {

pushAdditional: pushAdditional,

unshiftQuery: unshiftQuery,

createTable: buildTable('create'),

createTableIfNotExists: buildTable('createIfNot'),
Expand Down
24 changes: 24 additions & 0 deletions src/schema/helpers.js
Expand Up @@ -34,3 +34,27 @@ export function pushAdditional(fn) {
fn.call(child, tail(arguments));
this.sequence.additional = (this.sequence.additional || []).concat(child.sequence);
}

// Unshift a new query onto the compiled "sequence" stack,
// creating a new formatter, returning the compiler.
export function unshiftQuery(query) {
if (!query) return;
if (isString(query)) {
query = {sql: query};
}
if (!query.bindings) {
query.bindings = this.formatter.bindings;
}
this.sequence.unshift(query);

let builder;
if (this instanceof ColumnCompiler) {
builder = this.columnBuilder;
} else if (this instanceof TableCompiler) {
builder = this.tableBuilder;
} else if (this instanceof SchemaCompiler) {
builder = this.builder;
}

this.formatter = this.client.formatter(builder);
}
4 changes: 3 additions & 1 deletion src/schema/tablecompiler.js
Expand Up @@ -2,7 +2,7 @@

// Table Compiler
// -------
import { pushAdditional, pushQuery } from './helpers';
import { pushAdditional, pushQuery, unshiftQuery } from './helpers';
import * as helpers from '../helpers';
import { groupBy, reduce, map, first, tail, isEmpty, indexOf, isArray, isUndefined } from 'lodash'

Expand All @@ -23,6 +23,8 @@ TableCompiler.prototype.pushQuery = pushQuery

TableCompiler.prototype.pushAdditional = pushAdditional

TableCompiler.prototype.unshiftQuery = unshiftQuery

// Convert the tableCompiler toSQL
TableCompiler.prototype.toSQL = function () {
this[this.method]();
Expand Down
40 changes: 33 additions & 7 deletions test/integration/schema/index.js
Expand Up @@ -84,7 +84,7 @@ module.exports = function(knex) {
const table_name = 'increments_columns_1_test';
const expected_column = 'id'
const expected_comment = 'comment_1';

return knex.raw('SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = ?',[table_name]).then(function(res) {
const column_oid = res.rows[0].oid;

Expand All @@ -106,13 +106,13 @@ module.exports = function(knex) {
const table_name = 'increments_columns_2_test';
const expected_column = 'named_2';
const expected_comment = 'comment_2';

return knex.raw('SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = ?',[table_name]).then(function(res) {
const column_oid = res.rows[0].oid;

return knex.raw('SELECT pg_catalog.col_description(?,?);', [column_oid, '1']).then(function(_res) {
const comment = _res.rows[0].col_description;

return knex.raw('select column_name from INFORMATION_SCHEMA.COLUMNS where table_name = ?;', table_name).then((res) => {
const column_name = res.rows[0].column_name;

Expand Down Expand Up @@ -161,7 +161,7 @@ module.exports = function(knex) {
TABLE_NAME = ? AND
COLUMN_NAME = ?
`

return knex.raw(query,[table_name, expected_column]).then(function(res) {
const comment = res[0][0].COLUMN_COMMENT
expect(comment).to.equal(expected_comment)
Expand All @@ -183,15 +183,41 @@ module.exports = function(knex) {
TABLE_NAME = ? AND
COLUMN_NAME = ?
`

return knex.raw(query,[table_name, expected_column]).then(function(res) {
const comment = res[0][0].COLUMN_COMMENT
expect(comment).to.equal(expected_comment)
})

});
});

describe('enum - postgres', function() {
if (!knex || !knex.client || !(/postgres/i.test(knex.client.dialect))) {
return Promise.resolve();
}

afterEach(function() {
return knex.schema.dropTableIfExists('native_enum_test').raw('DROP TYPE "foo_type"');
});

it('uses native type when useNative is specified', function() {
return knex.schema
.createTable('native_enum_test', function(table) {
table.enum('foo_column', ['a', 'b', 'c'], {
useNative: true,
enumName: 'foo_type'
}).notNull();
table.uuid('id').notNull();
}).testSql(function(tester) {
tester('pg', [
'create type "foo_type" as enum (\'a\', \'b\', \'c\')',
'create table "native_enum_test" ("foo_column" "foo_type" not null, "id" uuid not null)'
]);
});
});
})

it('Callback function must be supplied', function() {
expect(function() {
knex.schema.createTable('callback_must_be_supplied').toString()
Expand Down
12 changes: 12 additions & 0 deletions test/unit/schema/postgres.js
Expand Up @@ -541,6 +541,18 @@ describe("PostgreSQL SchemaBuilder", function() {
expect(tableSql[0].sql).to.equal('alter table "users" add column "foo" text check ("foo" in (\'bar\', \'baz\'))');
});

it("adding enum with useNative", function() {
tableSql = client.schemaBuilder().table('users', function(table) {
table.enu('foo', ['bar', 'baz'], {
useNative: true,
enumName: 'foo_type'
}).notNullable();
}).toSQL();
equal(2, tableSql.length);
expect(tableSql[0].sql).to.equal('create type "foo_type" as enum (\'bar\', \'baz\')')
expect(tableSql[1].sql).to.equal('alter table "users" add column "foo" "foo_type" not null');
});

it("adding date", function() {
tableSql = client.schemaBuilder().table('users', function(table) {
table.date('foo');
Expand Down