Repository: knex/knex
Pull request #4821 (2021-11-10 19:28:06)

Add columns in create table like #4820
- Closes https://github.com/knex/knex/issues/4820
- Callback for create table like is in index.d.ts Typescript but not implemented.
- Now, callback add columns in the new created table (add columns in the query or generate alter table queries).


Patch:
diff --git a/lib/dialects/mssql/schema/mssql-tablecompiler.js b/lib/dialects/mssql/schema/mssql-tablecompiler.js
index 3714ede3..3c5bc9db 100644
--- a/lib/dialects/mssql/schema/mssql-tablecompiler.js
+++ b/lib/dialects/mssql/schema/mssql-tablecompiler.js
@@ -36,6 +36,9 @@ class TableCompiler_MSSQL extends TableCompiler {
     if (this.single.comment) {
       this.comment(this.single.comment);
     }
+    if (like) {
+      this.addColumns(columns, this.addColumnsPrefix);
+    }
   }
 
   comment(/** @type {string} */ comment) {
diff --git a/lib/dialects/mysql/schema/mysql-tablecompiler.js b/lib/dialects/mysql/schema/mysql-tablecompiler.js
index 3e66057e..9765b17e 100644
--- a/lib/dialects/mysql/schema/mysql-tablecompiler.js
+++ b/lib/dialects/mysql/schema/mysql-tablecompiler.js
@@ -50,6 +50,9 @@ class TableCompiler_MySQL extends TableCompiler {
     }
 
     this.pushQuery(sql);
+    if (like) {
+      this.addColumns(columns, this.addColumnsPrefix);
+    }
   }
 
   // Compiles the comment on the table.
diff --git a/lib/dialects/oracle/schema/oracle-tablecompiler.js b/lib/dialects/oracle/schema/oracle-tablecompiler.js
index eb33d822..2dbe6944 100644
--- a/lib/dialects/oracle/schema/oracle-tablecompiler.js
+++ b/lib/dialects/oracle/schema/oracle-tablecompiler.js
@@ -66,6 +66,9 @@ class TableCompiler_Oracle extends TableCompiler {
       bindings: columns.bindings,
     });
     if (this.single.comment) this.comment(this.single.comment);
+    if (like) {
+      this.addColumns(columns, this.addColumnsPrefix);
+    }
   }
 
   // Compiles the comment on the table.
diff --git a/lib/dialects/postgres/schema/pg-tablecompiler.js b/lib/dialects/postgres/schema/pg-tablecompiler.js
index dae03938..d9223bde 100644
--- a/lib/dialects/postgres/schema/pg-tablecompiler.js
+++ b/lib/dialects/postgres/schema/pg-tablecompiler.js
@@ -49,7 +49,11 @@ class TableCompiler_PG extends TableCompiler {
       createStatement +
       this.tableName() +
       (like && this.tableNameLike()
-        ? ' (like ' + this.tableNameLike() + ' including all)'
+        ? ' (like ' +
+          this.tableNameLike() +
+          ' including all' +
+          (columns.sql.length ? ', ' + columns.sql.join(', ') : '') +
+          ')'
         : columnsSql);
     if (this.single.inherits)
       sql += ` inherits (${this.formatter.wrap(this.single.inherits)})`;
diff --git a/lib/dialects/redshift/schema/redshift-tablecompiler.js b/lib/dialects/redshift/schema/redshift-tablecompiler.js
index 94c0b98d..67308b6b 100644
--- a/lib/dialects/redshift/schema/redshift-tablecompiler.js
+++ b/lib/dialects/redshift/schema/redshift-tablecompiler.js
@@ -45,6 +45,9 @@ class TableCompiler_Redshift extends TableCompiler_PG {
     });
     const hasComment = has(this.single, 'comment');
     if (hasComment) this.comment(this.single.comment);
+    if (like) {
+      this.addColumns(columns, this.addColumnsPrefix);
+    }
   }
 
   primary(columns, constraintName) {
diff --git a/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js b/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js
index 4331fbb4..0ff6f538 100644
--- a/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js
+++ b/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js
@@ -30,6 +30,10 @@ class TableCompiler_SQLite3 extends TableCompiler {
       sql += ')';
     }
     this.pushQuery(sql);
+
+    if (like) {
+      this.addColumns(columns, this.addColumnsPrefix);
+    }
   }
 
   addColumns(columns, prefix, colCompilers) {


Test patch:
diff --git a/test/integration2/schema/misc.spec.js b/test/integration2/schema/misc.spec.js
index 6a871b2c..65c4cc77 100644
--- a/test/integration2/schema/misc.spec.js
+++ b/test/integration2/schema/misc.spec.js
@@ -321,6 +321,62 @@ describe('Schema (misc)', () => {
                 expect(Object.keys(res)).to.have.all.members(['id', 'data']);
               });
           });
+
+          it('copy table with additionnal column', async () => {
+            await knex.schema.dropTableIfExists('table_copied');
+            await knex.schema
+              .createTableLike(
+                'table_copied',
+                'table_to_copy',
+                function (table) {
+                  table.text('add_col');
+                  table.integer('add_num_col');
+                }
+              )
+              .testSql((tester) => {
+                tester('mysql', [
+                  'create table `table_copied` like `table_to_copy`',
+                  'alter table `table_copied` add `add_col` text, add `add_num_col` int',
+                ]);
+                tester(
+                  ['pg', 'cockroachdb'],
+                  [
+                    'create table "table_copied" (like "table_to_copy" including all, "add_col" text, "add_num_col" integer)',
+                  ]
+                );
+                tester('pg-redshift', [
+                  'create table "table_copied" (like "table_to_copy")',
+                  'alter table "table_copied" add column "add_col" varchar(max)',
+                  'alter table "table_copied" add column "add_num_col" integer',
+                ]);
+                tester('sqlite3', [
+                  'create table `table_copied` as select * from `table_to_copy` where 0=1',
+                  'alter table `table_copied` add column `add_col` text',
+                  'alter table `table_copied` add column `add_num_col` integer',
+                ]);
+                tester('oracledb', [
+                  'create table "table_copied" as (select * from "table_to_copy" where 0=1)',
+                  'alter table "table_copied" add ("add_col" clob, "add_num_col" integer)',
+                ]);
+                tester('mssql', [
+                  'SELECT * INTO [table_copied] FROM [table_to_copy] WHERE 0=1',
+                  'ALTER TABLE [table_copied] ADD [add_col] nvarchar(max), [add_num_col] int',
+                ]);
+              });
+
+            expect(await knex.schema.hasTable('table_copied')).to.equal(true);
+
+            await knex('table_copied')
+              .columnInfo()
+              .then((res) => {
+                expect(Object.keys(res)).to.have.all.members([
+                  'id',
+                  'data',
+                  'add_col',
+                  'add_num_col',
+                ]);
+              });
+          });
         });
 
         describe('increments types - postgres', () => {
diff --git a/test/unit/schema-builder/mssql.js b/test/unit/schema-builder/mssql.js
index b146f17b..e07fc86b 100644
--- a/test/unit/schema-builder/mssql.js
+++ b/test/unit/schema-builder/mssql.js
@@ -50,6 +50,23 @@ describe('MSSQL SchemaBuilder', function () {
     );
   });
 
+  it('create table like another with additionnal columns', function () {
+    tableSql = client
+      .schemaBuilder()
+      .createTableLike('users_like', 'users', function (table) {
+        table.text('add_col');
+        table.integer('numeric_col');
+      })
+      .toSQL();
+    expect(tableSql.length).to.equal(2);
+    expect(tableSql[0].sql).to.equal(
+      'SELECT * INTO [users_like] FROM [users] WHERE 0=1'
+    );
+    expect(tableSql[1].sql).to.equal(
+      'ALTER TABLE [users_like] ADD [add_col] nvarchar(max), [numeric_col] int'
+    );
+  });
+
   describe('views', function () {
     let knexMssql;
 
diff --git a/test/unit/schema-builder/mysql.js b/test/unit/schema-builder/mysql.js
index a641179e..2af59448 100644
--- a/test/unit/schema-builder/mysql.js
+++ b/test/unit/schema-builder/mysql.js
@@ -46,6 +46,23 @@ module.exports = function (dialect) {
       );
     });
 
+    it('create table like another with additionnal columns', function () {
+      tableSql = client
+        .schemaBuilder()
+        .createTableLike('users_like', 'users', function (table) {
+          table.text('add_col');
+          table.integer('numeric_col');
+        })
+        .toSQL();
+      expect(tableSql.length).to.equal(2);
+      expect(tableSql[0].sql).to.equal(
+        'create table `users_like` like `users`'
+      );
+      expect(tableSql[1].sql).to.equal(
+        'alter table `users_like` add `add_col` text, add `numeric_col` int'
+      );
+    });
+
     it('test basic create table with incrementing without primary key', function () {
       tableSql = client
         .schemaBuilder()
diff --git a/test/unit/schema-builder/oracledb.js b/test/unit/schema-builder/oracledb.js
index 1cbde61d..fb52e472 100644
--- a/test/unit/schema-builder/oracledb.js
+++ b/test/unit/schema-builder/oracledb.js
@@ -35,6 +35,23 @@ describe('OracleDb SchemaBuilder', function () {
     );
   });
 
+  it('test create table like with additionnal columns', function () {
+    tableSql = client
+      .schemaBuilder()
+      .createTableLike('users_like', 'users', function (table) {
+        table.text('add_col');
+        table.integer('add_num_col');
+      });
+
+    expect(tableSql.toSQL().length).to.equal(2);
+    expect(tableSql.toSQL()[0].sql).to.equal(
+      'create table "users_like" as (select * from "users" where 0=1)'
+    );
+    expect(tableSql.toSQL()[1].sql).to.equal(
+      'alter table "users_like" add ("add_col" clob, "add_num_col" integer)'
+    );
+  });
+
   describe('views', function () {
     let knexOracleDb;
 
diff --git a/test/unit/schema-builder/postgres.js b/test/unit/schema-builder/postgres.js
index 2caee2ac..e9ef958d 100644
--- a/test/unit/schema-builder/postgres.js
+++ b/test/unit/schema-builder/postgres.js
@@ -123,6 +123,20 @@ describe('PostgreSQL SchemaBuilder', function () {
     );
   });
 
+  it('create table like another with additionnal columns', function () {
+    tableSql = client
+      .schemaBuilder()
+      .createTableLike('users_like', 'users', function (table) {
+        table.text('add_col');
+        table.integer('numeric_col');
+      })
+      .toSQL();
+    expect(tableSql.length).to.equal(1);
+    expect(tableSql[0].sql).to.equal(
+      'create table "users_like" (like "users" including all, "add_col" text, "numeric_col" integer)'
+    );
+  });
+
   it('basic alter table', function () {
     tableSql = client
       .schemaBuilder()
diff --git a/test/unit/schema-builder/redshift.js b/test/unit/schema-builder/redshift.js
index 1beb76d8..c3857947 100644
--- a/test/unit/schema-builder/redshift.js
+++ b/test/unit/schema-builder/redshift.js
@@ -38,6 +38,26 @@ describe('Redshift SchemaBuilder', function () {
     );
   });
 
+  it('create table like another with additional columns', function () {
+    tableSql = client
+      .schemaBuilder()
+      .createTableLike('users_like', 'users', function (table) {
+        table.text('add_col');
+        table.integer('add_num_col');
+      })
+      .toSQL();
+    expect(tableSql.length).to.equal(3);
+    expect(tableSql[0].sql).to.equal(
+      'create table "users_like" (like "users")'
+    );
+    expect(tableSql[1].sql).to.equal(
+      'alter table "users_like" add column "add_col" varchar(max)'
+    );
+    expect(tableSql[2].sql).to.equal(
+      'alter table "users_like" add column "add_num_col" integer'
+    );
+  });
+
   it('basic alter table', function () {
     tableSql = client
       .schemaBuilder()
diff --git a/test/unit/schema-builder/sqlite3.js b/test/unit/schema-builder/sqlite3.js
index e18cae0c..454db567 100644
--- a/test/unit/schema-builder/sqlite3.js
+++ b/test/unit/schema-builder/sqlite3.js
@@ -50,6 +50,26 @@ describe('SQLite SchemaBuilder', function () {
     );
   });
 
+  it('test create table like with additionnal columns', function () {
+    tableSql = client
+      .schemaBuilder()
+      .createTableLike('users_like', 'users', function (table) {
+        table.text('add_col');
+        table.integer('add_num_col');
+      });
+
+    expect(tableSql.toSQL().length).to.equal(3);
+    expect(tableSql.toSQL()[0].sql).to.equal(
+      'create table `users_like` as select * from `users` where 0=1'
+    );
+    expect(tableSql.toSQL()[1].sql).to.equal(
+      'alter table `users_like` add column `add_col` text'
+    );
+    expect(tableSql.toSQL()[2].sql).to.equal(
+      'alter table `users_like` add column `add_num_col` integer'
+    );
+  });
+
   describe('views', function () {
     let knexSqlite3;