diff --git a/lib/schema/tablebuilder.js b/lib/schema/tablebuilder.js index a561b0a125..b77f9eced3 100644 --- a/lib/schema/tablebuilder.js +++ b/lib/schema/tablebuilder.js @@ -10,7 +10,7 @@ const each = require('lodash/each'); const extend = require('lodash/extend'); const toArray = require('lodash/toArray'); const helpers = require('../util/helpers'); -const { isString, isFunction } = require('../util/is'); +const { isString, isFunction, isObject } = require('../util/is'); class TableBuilder { constructor(client, method, tableName, tableNameLike, fn) { @@ -50,16 +50,20 @@ class TableBuilder { } // The "timestamps" call is really just sets the `created_at` and `updated_at` columns. - timestamps() { - const method = arguments[0] === true ? 'timestamp' : 'datetime'; - const createdAt = this[method]('created_at'); - const updatedAt = this[method]('updated_at'); - if (arguments[1] === true) { + + timestamps(useTimestamps, defaultToNow, useCamelCase) { + if (isObject(useTimestamps)) { + ({ useTimestamps, defaultToNow, useCamelCase } = useTimestamps); + } + const method = useTimestamps === true ? 'timestamp' : 'datetime'; + const createdAt = this[method](useCamelCase ? 'createdAt' : 'created_at'); + const updatedAt = this[method](useCamelCase ? 'updatedAt' : 'updated_at'); + + if (defaultToNow === true) { const now = this.client.raw('CURRENT_TIMESTAMP'); createdAt.notNullable().defaultTo(now); updatedAt.notNullable().defaultTo(now); } - return; } // Set the comment value for a table, they're only allowed to be called @@ -290,7 +294,12 @@ const AlterMethods = { }, dropTimestamps() { - return this.dropColumns(['created_at', 'updated_at']); + // arguments[0] = useCamelCase + return this.dropColumns( + arguments[0] === true + ? ['createdAt', 'updatedAt'] + : ['created_at', 'updated_at'] + ); }, setNullable(column) { diff --git a/test/integration/logger.js b/test/integration/logger.js index 58ef497c6e..af068b0a90 100644 --- a/test/integration/logger.js +++ b/test/integration/logger.js @@ -98,7 +98,12 @@ module.exports = function (knex) { return _.reduce( val, function (memo, val, key) { - if (_.includes(['created_at', 'updated_at'], key)) { + if ( + _.includes( + ['created_at', 'updated_at', 'createdAt', 'updatedAt'], + key + ) + ) { memo[key] = TEST_TIMESTAMP; } else if (_.includes(['dummy_id'], key)) { memo[key] = TEST_ID; diff --git a/test/integration2/schema/misc.spec.js b/test/integration2/schema/misc.spec.js index c747298489..4a3a918d39 100644 --- a/test/integration2/schema/misc.spec.js +++ b/test/integration2/schema/misc.spec.js @@ -728,6 +728,48 @@ describe('Schema (misc)', () => { ]); })); + it('create table with timestamps options', async () => { + await knex.schema + .createTable('test_table_timestamp', (table) => { + if (isMysql(knex)) table.engine('InnoDB'); + table.bigIncrements('id'); + table.timestamps({ + useTimestamps: false, + defaultToNow: true, + useCamelCase: true, + }); + }) + .testSql((tester) => { + tester('mysql', [ + 'create table `test_table_timestamp` (`id` bigint unsigned not null auto_increment primary key, `createdAt` datetime not null default CURRENT_TIMESTAMP, `updatedAt` datetime not null default CURRENT_TIMESTAMP) default character set utf8 engine = InnoDB', + ]); + tester( + ['pg', 'cockroachdb'], + [ + 'create table "test_table_timestamp" ("id" bigserial primary key, "createdAt" timestamptz not null default CURRENT_TIMESTAMP, "updatedAt" timestamptz not null default CURRENT_TIMESTAMP)', + ] + ); + tester('pg-redshift', [ + 'create table "test_table_timestamp" ("id" bigint identity(1,1) primary key not null, "createdAt" timestamptz not null default CURRENT_TIMESTAMP, "updatedAt" timestamptz not null default CURRENT_TIMESTAMP)', + ]); + tester('sqlite3', [ + 'create table `test_table_timestamp` (`id` integer not null primary key autoincrement, `createdAt` datetime not null default CURRENT_TIMESTAMP, `updatedAt` datetime not null default CURRENT_TIMESTAMP)', + ]); + tester('oracledb', [ + `create table "test_table_timestamp" + ( + "id" number(20, 0) not null primary key, + "created_at" timestamp with local time zone not null default CURRENT_TIMESTAMP, + "updated_at" timestamp with local time zone not null default CURRENT_TIMESTAMP + )`, + ]); + tester('mssql', [ + 'CREATE TABLE [test_table_timestamp] ([id] bigint identity(1,1) not null primary key, [createdAt] datetime2 not null CONSTRAINT [test_table_timestamp_createdat_default] DEFAULT CURRENT_TIMESTAMP, [updatedAt] datetime2 not null CONSTRAINT [test_table_timestamp_updatedat_default] DEFAULT CURRENT_TIMESTAMP)', + ]); + }); + await knex.schema.dropTableIfExists('test_table_timestamp'); + }); + it('is possible to set the db engine with the table.engine', () => knex.schema .createTable('test_table_two', (table) => { diff --git a/test/unit/schema-builder/postgres.js b/test/unit/schema-builder/postgres.js index ab11baac24..23343cfaea 100644 --- a/test/unit/schema-builder/postgres.js +++ b/test/unit/schema-builder/postgres.js @@ -1646,6 +1646,23 @@ describe('PostgreSQL SchemaBuilder', function () { ); }); + it('adding timestamps with options object', () => { + tableSql = client + .schemaBuilder() + .table('users', (table) => { + table.timestamps({ + useTimestamps: true, + defaultToNow: true, + useCamelCase: true, + }); + }) + .toSQL(); + equal(1, tableSql.length); + expect(tableSql[0].sql).to.equal( + 'alter table "users" add column "createdAt" timestamptz not null default CURRENT_TIMESTAMP, add column "updatedAt" timestamptz not null default CURRENT_TIMESTAMP' + ); + }); + it('adding binary', function () { tableSql = client .schemaBuilder() diff --git a/types/index.d.ts b/types/index.d.ts index b37bf63d86..53bd9f7c6e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2034,8 +2034,12 @@ export declare namespace Knex { /** @deprecated */ timestamp(columnName: string, withoutTz?: boolean, precision?: number): ColumnBuilder; timestamps( - useTimestampType?: boolean, - makeDefaultNow?: boolean + useTimestamps?: boolean, + defaultToNow?: boolean, + useCamelCase?: boolean + ): ColumnBuilder; + timestamps( + options?: Readonly<{useTimestamps?: boolean, defaultToNow?: boolean, useCamelCase?: boolean}> ): void; geometry(columnName: string): ColumnBuilder; geography(columnName: string): ColumnBuilder; @@ -2083,7 +2087,7 @@ export declare namespace Knex { dropUnique(columnNames: readonly (string | Raw)[], indexName?: string): TableBuilder; dropPrimary(constraintName?: string): TableBuilder; dropIndex(columnNames: string | readonly (string | Raw)[], indexName?: string): TableBuilder; - dropTimestamps(): TableBuilder; + dropTimestamps(useCamelCase?: boolean): TableBuilder; queryContext(context: any): TableBuilder; }