From dd6ff481a6a4b3c8d800765569b4567c2d816296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=22foxie=22=20Bren=C4=8Di=C4=8D?= Date: Fri, 26 Nov 2021 15:46:58 +0100 Subject: [PATCH 1/3] fix: :bug: Changed createViewOrReplace sql statement and added tests --- .gitignore | 2 + lib/schema/viewcompiler.js | 4 +- test/integration2/schema/views.spec.js | 75 ++++++++++++++++++++++++++ test/unit/schema-builder/mssql.js | 13 +++++ test/unit/schema-builder/mysql.js | 15 +++++- test/unit/schema-builder/oracledb.js | 15 +++++- test/unit/schema-builder/postgres.js | 15 +++++- test/unit/schema-builder/redshift.js | 15 +++++- 8 files changed, 148 insertions(+), 6 deletions(-) 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/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..ff61c3a14c 100644 --- a/test/integration2/schema/views.spec.js +++ b/test/integration2/schema/views.spec.js @@ -122,6 +122,81 @@ describe('Views', () => { }); }); + it('create or replace view', async () => { + 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('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) { + 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 () => { + 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( + ['sqlite3', 'mysql'], + [ + "create or replace 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..21ed39d782 100644 --- a/test/unit/schema-builder/mysql.js +++ b/test/unit/schema-builder/mysql.js @@ -157,7 +157,20 @@ module.exports = function (dialect) { .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` (`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(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + "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..34a74e497e 100644 --- a/test/unit/schema-builder/oracledb.js +++ b/test/unit/schema-builder/oracledb.js @@ -99,7 +99,20 @@ describe('OracleDb SchemaBuilder', function () { .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" ("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(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + 'create or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); }); diff --git a/test/unit/schema-builder/postgres.js b/test/unit/schema-builder/postgres.js index 8aff022c2e..62f98d4ff4 100644 --- a/test/unit/schema-builder/postgres.js +++ b/test/unit/schema-builder/postgres.js @@ -257,7 +257,20 @@ describe('PostgreSQL SchemaBuilder', function () { .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" ("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 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..2065c8e621 100644 --- a/test/unit/schema-builder/redshift.js +++ b/test/unit/schema-builder/redshift.js @@ -122,7 +122,20 @@ describe('Redshift SchemaBuilder', function () { .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" ("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(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + 'create or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); }); From 808a5f59842c8cca74a5e84dcb66191bde0f34a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=22foxie=22=20Bren=C4=8Di=C4=8D?= Date: Fri, 26 Nov 2021 17:25:26 +0100 Subject: [PATCH 2/3] feat: :sparkles: Support for createViewOrReplace for sqlite3 --- .../sqlite3/schema/sqlite-viewcompiler.js | 33 +++++++++++++++++-- test/integration2/schema/views.spec.js | 16 ++++++++- test/unit/schema-builder/sqlite3.js | 32 +++++++++++++----- 3 files changed, 70 insertions(+), 11 deletions(-) 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/test/integration2/schema/views.spec.js b/test/integration2/schema/views.spec.js index ff61c3a14c..a4b3c90f9e 100644 --- a/test/integration2/schema/views.spec.js +++ b/test/integration2/schema/views.spec.js @@ -143,6 +143,13 @@ describe('Views', () => { "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'", ]); @@ -175,11 +182,18 @@ describe('Views', () => { ] ); tester( - ['sqlite3', 'mysql'], + ['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'", ]); diff --git a/test/unit/schema-builder/sqlite3.js b/test/unit/schema-builder/sqlite3.js index 0cebd7b123..a610fc5cac 100644 --- a/test/unit/schema-builder/sqlite3.js +++ b/test/unit/schema-builder/sqlite3.js @@ -110,14 +110,30 @@ 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(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + "DROP VIEW IF EXISTS `adults`; 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(); + equal(1, viewSql.length); + expect(viewSql[0].sql).to.equal( + "DROP VIEW IF EXISTS `adults`; CREATE VIEW `adults` AS select `name` from `users` where `age` > '18'" + ); }); it('create view with check options', async function () { From 32ef035e99a870f12a14ff4c5accf312949874e8 Mon Sep 17 00:00:00 2001 From: Olivier Cavadenti Date: Thu, 9 Dec 2021 19:17:32 +0100 Subject: [PATCH 3/3] PR returns --- test/integration2/schema/views.spec.js | 35 ++++++++++++++++++++++++++ test/unit/schema-builder/mysql.js | 4 +-- test/unit/schema-builder/oracledb.js | 6 ++--- test/unit/schema-builder/postgres.js | 2 +- test/unit/schema-builder/redshift.js | 12 ++++----- test/unit/schema-builder/sqlite3.js | 14 ++++++----- 6 files changed, 55 insertions(+), 18 deletions(-) diff --git a/test/integration2/schema/views.spec.js b/test/integration2/schema/views.spec.js index a4b3c90f9e..f4e258de26 100644 --- a/test/integration2/schema/views.spec.js +++ b/test/integration2/schema/views.spec.js @@ -123,6 +123,23 @@ 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']); @@ -160,6 +177,7 @@ describe('Views', () => { .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'); @@ -168,6 +186,23 @@ describe('Views', () => { }); 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( diff --git a/test/unit/schema-builder/mysql.js b/test/unit/schema-builder/mysql.js index 21ed39d782..66df0b2325 100644 --- a/test/unit/schema-builder/mysql.js +++ b/test/unit/schema-builder/mysql.js @@ -155,7 +155,7 @@ 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'" ); @@ -168,7 +168,7 @@ 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` 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 34a74e497e..dfedfbf8ea 100644 --- a/test/unit/schema-builder/oracledb.js +++ b/test/unit/schema-builder/oracledb.js @@ -97,7 +97,7 @@ 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\'' ); @@ -110,7 +110,7 @@ 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" as select "name" from "users" where "age" > \'18\'' ); @@ -125,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 62f98d4ff4..c8ebd05199 100644 --- a/test/unit/schema-builder/postgres.js +++ b/test/unit/schema-builder/postgres.js @@ -255,7 +255,7 @@ describe('PostgreSQL SchemaBuilder', function () { view.as(knexPg('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\'' ); diff --git a/test/unit/schema-builder/redshift.js b/test/unit/schema-builder/redshift.js index 2065c8e621..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,7 +120,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 or replace view "adults" ("name") as select "name" from "users" where "age" > \'18\'' ); @@ -133,7 +133,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 or replace view "adults" as select "name" from "users" where "age" > \'18\'' ); @@ -148,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' ); @@ -161,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 a610fc5cac..e7ff9a00f9 100644 --- a/test/unit/schema-builder/sqlite3.js +++ b/test/unit/schema-builder/sqlite3.js @@ -117,9 +117,10 @@ describe('SQLite SchemaBuilder', function () { view.as(knexSqlite3('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); - expect(viewSql[0].sql).to.equal( - "DROP VIEW IF EXISTS `adults`; CREATE VIEW `adults` (`name`) AS select `name` from `users` where `age` > '18'" + 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'" ); }); @@ -130,9 +131,10 @@ describe('SQLite SchemaBuilder', function () { view.as(knexSqlite3('users').select('name').where('age', '>', '18')); }) .toSQL(); - equal(1, viewSql.length); - expect(viewSql[0].sql).to.equal( - "DROP VIEW IF EXISTS `adults`; CREATE VIEW `adults` AS select `name` from `users` where `age` > '18'" + 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'" ); });