diff --git a/.gitignore b/.gitignore index 9115dca380..b0d47e0f80 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ test/coverage/* #VsCode workspace .vscode /coverage/ + +test/integration/migrate/migration/ diff --git a/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js b/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js index 1359d77eed..77f42244e4 100644 --- a/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +++ b/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js @@ -1,11 +1,40 @@ /* eslint max-len: 0 */ const ViewCompiler = require('../../../schema/viewcompiler.js'); +const { + columnize: columnize_, +} = require('../../../formatter/wrappingFormatter'); -class ViewCompiler_PG extends ViewCompiler { +class ViewCompiler_SQLite3 extends ViewCompiler { constructor(client, viewCompiler) { super(client, viewCompiler); } + createOrReplace() { + const columns = this.columns; + const selectQuery = this.selectQuery.toString(); + const viewName = this.viewName(); + + const columnList = columns + ? ' (' + + columnize_( + columns, + this.viewBuilder, + this.client, + this.bindingsHolder + ) + + ')' + : ''; + + const dropSql = `drop view if exists ${viewName}`; + const createSql = `create view ${viewName}${columnList} as ${selectQuery}`; + + this.pushQuery({ + sql: dropSql, + }); + this.pushQuery({ + sql: createSql, + }); + } } -module.exports = ViewCompiler_PG; +module.exports = ViewCompiler_SQLite3; diff --git a/lib/schema/viewcompiler.js b/lib/schema/viewcompiler.js index ea2efa2350..19a9a01771 100644 --- a/lib/schema/viewcompiler.js +++ b/lib/schema/viewcompiler.js @@ -52,8 +52,8 @@ class ViewCompiler { const createStatement = 'create ' + (materialized ? 'materialized ' : '') + - 'view ' + - (replace ? 'or replace ' : ''); + (replace ? 'or replace ' : '') + + 'view '; const columnList = columns ? ' (' + columnize_( diff --git a/test/integration2/schema/views.spec.js b/test/integration2/schema/views.spec.js index ea6305b136..f4e258de26 100644 --- a/test/integration2/schema/views.spec.js +++ b/test/integration2/schema/views.spec.js @@ -122,6 +122,130 @@ describe('Views', () => { }); }); + it('create or replace view', async () => { + // We create the view and test if all is ok + await knex.schema.createView('view_test', (view) => { + view.as(knex('table_view').select('a', 'b')); + }); + await knex + .select(['a', 'b']) + .from('view_test') + .then(function (results) { + expect(results.length).to.equal(3); + expect(results[0].a).to.be.equal('test'); + expect(results[1].a).to.be.equal('test2'); + expect(results[2].a).to.be.equal('test3'); + assertNumber(knex, results[0].b, 5); + assertNumber(knex, results[1].b, 12); + assertNumber(knex, results[2].b, 45); + }); + // Now we test that the new view is replaced + await knex.schema + .createViewOrReplace('view_test', function (view) { + view.columns(['a', 'b']); + view.as( + knex('table_view').select('a', 'b').where('b', '>', '10') + ); + }) + .testSql((tester) => { + tester( + ['pg', 'pg-redshift', 'cockroachdb', 'oracledb'], + [ + 'create or replace view "view_test" ("a", "b") as select "a", "b" from "table_view" where "b" > \'10\'', + ] + ); + tester( + ['mysql'], + [ + "create or replace view `view_test` (`a`, `b`) as select `a`, `b` from `table_view` where `b` > '10'", + ] + ); + tester( + ['sqlite3'], + [ + 'drop view if exists `view_test`', + "create view `view_test` (`a`, `b`) as select `a`, `b` from `table_view` where `b` > '10'", + ] + ); + tester('mssql', [ + "CREATE OR ALTER VIEW [view_test] ([a], [b]) AS select [a], [b] from [table_view] where [b] > '10'", + ]); + }); + + // We test if the select on the view works and if results are good + await knex + .select(['a', 'b']) + .from('view_test') + .then(function (results) { + expect(results.length).to.equal(2); + assertNumber(knex, results[0].b, 12); + assertNumber(knex, results[1].b, 45); + expect(results[0].a).to.be.equal('test2'); + expect(results[1].a).to.be.equal('test3'); + }); + }); + + it('create or replace view without columns', async () => { + // We create the view and test if all is ok + await knex.schema.createView('view_test', (view) => { + view.as(knex('table_view').select('a', 'b')); + }); + await knex + .select(['a', 'b']) + .from('view_test') + .then(function (results) { + expect(results.length).to.equal(3); + expect(results[0].a).to.be.equal('test'); + expect(results[1].a).to.be.equal('test2'); + expect(results[2].a).to.be.equal('test3'); + assertNumber(knex, results[0].b, 5); + assertNumber(knex, results[1].b, 12); + assertNumber(knex, results[2].b, 45); + }); + // Now we test that the new view is replaced + await knex.schema + .createViewOrReplace('view_test', function (view) { + view.as( + knex('table_view').select('a', 'b').where('b', '>', '10') + ); + }) + .testSql((tester) => { + tester( + ['pg', 'pg-redshift', 'cockroachdb', 'oracledb'], + [ + 'create or replace view "view_test" as select "a", "b" from "table_view" where "b" > \'10\'', + ] + ); + tester( + ['mysql'], + [ + "create or replace view `view_test` as select `a`, `b` from `table_view` where `b` > '10'", + ] + ); + tester( + ['sqlite3'], + [ + 'drop view if exists `view_test`', + "create view `view_test` as select `a`, `b` from `table_view` where `b` > '10'", + ] + ); + tester('mssql', [ + "CREATE OR ALTER VIEW [view_test] AS select [a], [b] from [table_view] where [b] > '10'", + ]); + }); + + // We test if the select on the view works and if results are good + await knex + .select(['a', 'b']) + .from('view_test') + .then(function (results) { + assertNumber(knex, results[0].b, 12); + assertNumber(knex, results[1].b, 45); + expect(results[0].a).to.be.equal('test2'); + expect(results[1].a).to.be.equal('test3'); + }); + }); + it('create materialized view', async function () { if (isMssql(knex) || isSQLite(knex) || isMysql(knex)) { return this.skip(); diff --git a/test/unit/schema-builder/mssql.js b/test/unit/schema-builder/mssql.js index 91649f22b2..a4ccd31e1c 100644 --- a/test/unit/schema-builder/mssql.js +++ b/test/unit/schema-builder/mssql.js @@ -118,6 +118,19 @@ describe('MSSQL SchemaBuilder', function () { ); }); + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexMssql('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + "CREATE OR ALTER VIEW [adults] AS select [name] from [users] where [age] > '18'" + ); + }); + it('create view with check options', async function () { expect(() => { client diff --git a/test/unit/schema-builder/mysql.js b/test/unit/schema-builder/mysql.js index 453914aa73..66df0b2325 100644 --- a/test/unit/schema-builder/mysql.js +++ b/test/unit/schema-builder/mysql.js @@ -155,9 +155,22 @@ module.exports = function (dialect) { view.as(knexMysql('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); + expect(viewSql.length).to.equal(1); + expect(viewSql[0].sql).to.equal( + "create or replace view `adults` (`name`) as select `name` from `users` where `age` > '18'" + ); + }); + + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexMysql('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + expect(viewSql.length).to.equal(1); expect(viewSql[0].sql).to.equal( - "create view or replace `adults` (`name`) as select `name` from `users` where `age` > '18'" + "create or replace view `adults` as select `name` from `users` where `age` > '18'" ); }); diff --git a/test/unit/schema-builder/oracledb.js b/test/unit/schema-builder/oracledb.js index dc07b5f85f..dfedfbf8ea 100644 --- a/test/unit/schema-builder/oracledb.js +++ b/test/unit/schema-builder/oracledb.js @@ -97,9 +97,22 @@ describe('OracleDb SchemaBuilder', function () { view.as(knexOracleDb('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); + expect(viewSql.length).to.equal(1); + expect(viewSql[0].sql).to.equal( + 'create or replace view "adults" ("name") as select "name" from "users" where "age" > \'18\'' + ); + }); + + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexOracleDb('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + expect(viewSql.length).to.equal(1); expect(viewSql[0].sql).to.equal( - 'create view or replace "adults" ("name") as select "name" from "users" where "age" > \'18\'' + 'create or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); }); @@ -112,7 +125,7 @@ describe('OracleDb SchemaBuilder', function () { view.checkOption(); }) .toSQL(); - equal(1, tableSql.length); + expect(tableSql.length).to.equal(1); expect(tableSql[0].sql).to.equal( 'create view "adults" ("name") as select "name" from "users" where "age" > \'18\' with check option' ); diff --git a/test/unit/schema-builder/postgres.js b/test/unit/schema-builder/postgres.js index 8aff022c2e..c8ebd05199 100644 --- a/test/unit/schema-builder/postgres.js +++ b/test/unit/schema-builder/postgres.js @@ -255,9 +255,22 @@ describe('PostgreSQL SchemaBuilder', function () { view.as(knexPg('users').select('name').where('age', '>', '18')); }) .toSQL(); + expect(viewSql.length).to.equal(1); + expect(viewSql[0].sql).to.equal( + 'create or replace view "adults" ("name") as select "name" from "users" where "age" > \'18\'' + ); + }); + + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexPg('users').select('name').where('age', '>', '18')); + }) + .toSQL(); equal(1, viewSql.length); expect(viewSql[0].sql).to.equal( - 'create view or replace "adults" ("name") as select "name" from "users" where "age" > \'18\'' + 'create or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); }); diff --git a/test/unit/schema-builder/redshift.js b/test/unit/schema-builder/redshift.js index 3d3b92dfd4..a4bdf464c0 100644 --- a/test/unit/schema-builder/redshift.js +++ b/test/unit/schema-builder/redshift.js @@ -93,7 +93,7 @@ describe('Redshift SchemaBuilder', function () { view.as(knexRedShift('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); + expect(viewSql.length).to.equal(1); expect(viewSql[0].sql).to.equal( 'create view "adults" ("name") as select "name" from "users" where "age" > \'18\'' ); @@ -106,7 +106,7 @@ describe('Redshift SchemaBuilder', function () { view.as(knexRedShift('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); + expect(viewSql.length).to.equal(1); expect(viewSql[0].sql).to.equal( 'create view "adults" as select "name" from "users" where "age" > \'18\'' ); @@ -120,9 +120,22 @@ describe('Redshift SchemaBuilder', function () { view.as(knexRedShift('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); + expect(viewSql.length).to.equal(1); expect(viewSql[0].sql).to.equal( - 'create view or replace "adults" ("name") as select "name" from "users" where "age" > \'18\'' + 'create or replace view "adults" ("name") as select "name" from "users" where "age" > \'18\'' + ); + }); + + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexRedShift('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + expect(viewSql.length).to.equal(1); + expect(viewSql[0].sql).to.equal( + 'create or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); }); @@ -135,7 +148,7 @@ describe('Redshift SchemaBuilder', function () { view.localCheckOption(); }) .toSQL(); - equal(1, viewSqlLocalCheck.length); + expect(viewSqlLocalCheck.length).to.equal(1); expect(viewSqlLocalCheck[0].sql).to.equal( 'create view "adults" ("name") as select "name" from "users" where "age" > \'18\' with local check option' ); @@ -148,7 +161,7 @@ describe('Redshift SchemaBuilder', function () { view.cascadedCheckOption(); }) .toSQL(); - equal(1, viewSqlCascadedCheck.length); + expect(viewSqlCascadedCheck.length).to.equal(1); expect(viewSqlCascadedCheck[0].sql).to.equal( 'create view "adults" ("name") as select "name" from "users" where "age" > \'18\' with cascaded check option' ); diff --git a/test/unit/schema-builder/sqlite3.js b/test/unit/schema-builder/sqlite3.js index 0cebd7b123..e7ff9a00f9 100644 --- a/test/unit/schema-builder/sqlite3.js +++ b/test/unit/schema-builder/sqlite3.js @@ -110,14 +110,32 @@ describe('SQLite SchemaBuilder', function () { }); it('create view or replace', async function () { - expect(() => { - tableSql = client - .schemaBuilder() - .view('users', function (view) { - view.column('oldName').rename('newName').defaultTo('10'); - }) - .toSQL(); - }).to.throw('rename column of views is not supported by this dialect.'); + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.columns(['name']); + view.as(knexSqlite3('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + expect(viewSql.length).to.equal(2); + expect(viewSql[0].sql).to.equal('drop view if exists `adults`'); + expect(viewSql[1].sql).to.equal( + "create view `adults` (`name`) as select `name` from `users` where `age` > '18'" + ); + }); + + it('create view or replace without columns', async function () { + const viewSql = client + .schemaBuilder() + .createViewOrReplace('adults', function (view) { + view.as(knexSqlite3('users').select('name').where('age', '>', '18')); + }) + .toSQL(); + expect(viewSql.length).to.equal(2); + expect(viewSql[0].sql).to.equal('drop view if exists `adults`'); + expect(viewSql[1].sql).to.equal( + "create view `adults` as select `name` from `users` where `age` > '18'" + ); }); it('create view with check options', async function () {