From 6e559ae86664a58f8e9a2a6cd380eba5e14f5ee5 Mon Sep 17 00:00:00 2001 From: ryanalbrecht Date: Wed, 12 Jun 2024 16:14:53 -0500 Subject: [PATCH] feat(SchemaBuilder): Added ability to specify precision for timestamp datatypes --- models/Grammars/BaseGrammar.cfc | 2 +- models/Grammars/OracleGrammar.cfc | 2 +- models/Grammars/SqlServerGrammar.cfc | 2 +- models/Schema/Blueprint.cfc | 2 +- models/Schema/Column.cfc | 4 +-- tests/resources/AbstractSchemaBuilderSpec.cfc | 13 +++++++++ tests/specs/Schema/MySQLSchemaBuilderSpec.cfc | 4 +++ .../specs/Schema/OracleSchemaBuilderSpec.cfc | 28 +++++++++++-------- .../Schema/PostgresSchemaBuilderSpec.cfc | 5 ++++ .../specs/Schema/SQLiteSchemaBuilderSpec.cfc | 4 +++ .../Schema/SqlServerSchemaBuilderSpec.cfc | 4 +++ 11 files changed, 52 insertions(+), 18 deletions(-) diff --git a/models/Grammars/BaseGrammar.cfc b/models/Grammars/BaseGrammar.cfc index a4dc172f..7e68f320 100644 --- a/models/Grammars/BaseGrammar.cfc +++ b/models/Grammars/BaseGrammar.cfc @@ -1643,7 +1643,7 @@ component displayname="Grammar" accessors="true" singleton { } function typeTimestamp( column ) { - return "TIMESTAMP"; + return "TIMESTAMP#isNull( column.getPrecision() ) ? "" : "(#column.getPrecision()#)"#"; } function typeTimestampTz( column ) { diff --git a/models/Grammars/OracleGrammar.cfc b/models/Grammars/OracleGrammar.cfc index 51370831..3b92b298 100644 --- a/models/Grammars/OracleGrammar.cfc +++ b/models/Grammars/OracleGrammar.cfc @@ -623,7 +623,7 @@ component extends="qb.models.Grammars.BaseGrammar" singleton { } function typeTimestamp( column ) { - return "DATE"; + return "TIMESTAMP#isNull( column.getPrecision() ) ? "" : "(#column.getPrecision()#)"#"; } function typeTimestampTz( column ) { diff --git a/models/Grammars/SqlServerGrammar.cfc b/models/Grammars/SqlServerGrammar.cfc index d7b338ad..94099acd 100644 --- a/models/Grammars/SqlServerGrammar.cfc +++ b/models/Grammars/SqlServerGrammar.cfc @@ -694,7 +694,7 @@ component extends="qb.models.Grammars.BaseGrammar" singleton accessors="true" { } function typeTimestamp( column ) { - return "DATETIME2"; + return "DATETIME2#isNull( column.getPrecision() ) ? "" : "(#column.getPrecision()#)"#"; } function typeTimestampTz( column ) { diff --git a/models/Schema/Blueprint.cfc b/models/Schema/Blueprint.cfc index b553d9f8..ddb2bb6e 100644 --- a/models/Schema/Blueprint.cfc +++ b/models/Schema/Blueprint.cfc @@ -272,7 +272,7 @@ component accessors="true" { return appendColumn( argumentCollection = arguments ); } - public Column function timestamp( required string name ) { + public Column function timestamp( required string name, numeric precision ) { arguments.type = "timestamp"; return appendColumn( argumentCollection = arguments ); } diff --git a/models/Schema/Column.cfc b/models/Schema/Column.cfc index b8bb9daa..98507792 100644 --- a/models/Schema/Column.cfc +++ b/models/Schema/Column.cfc @@ -199,8 +199,8 @@ component accessors="true" { * * @returns Column */ - public Column function withCurrent() { - setDefault( "CURRENT_TIMESTAMP" ); + public Column function withCurrent( numeric precision ) { + setDefault( "CURRENT_TIMESTAMP#isNull( arguments.precision ) ? "" : "(#arguments.precision#)"#" ); return this; } diff --git a/tests/resources/AbstractSchemaBuilderSpec.cfc b/tests/resources/AbstractSchemaBuilderSpec.cfc index e5694953..aad6ca55 100644 --- a/tests/resources/AbstractSchemaBuilderSpec.cfc +++ b/tests/resources/AbstractSchemaBuilderSpec.cfc @@ -788,6 +788,19 @@ component extends="testbox.system.BaseSpec" { }, timestamp() ); } ); + it( "timestampPrecision", function() { + testCase( function( schema ) { + return schema.create( + "posts", + function( table ) { + table.timestamp( "posted_date", 6 ); + }, + {}, + false + ); + }, timestampPrecision( 6 ) ); + } ); + it( "timestampWithNullable", function() { testCase( function( schema ) { return schema.create( diff --git a/tests/specs/Schema/MySQLSchemaBuilderSpec.cfc b/tests/specs/Schema/MySQLSchemaBuilderSpec.cfc index 2a3da291..de974954 100644 --- a/tests/specs/Schema/MySQLSchemaBuilderSpec.cfc +++ b/tests/specs/Schema/MySQLSchemaBuilderSpec.cfc @@ -258,6 +258,10 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { return [ "CREATE TABLE `posts` (`posted_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)" ]; } + function timestampPrecision() { + return [ "CREATE TABLE `posts` (`posted_date` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP)" ]; + } + function timestampWithNullable() { return [ "CREATE TABLE `posts` (`posted_date` TIMESTAMP NULL DEFAULT NULL)" ]; } diff --git a/tests/specs/Schema/OracleSchemaBuilderSpec.cfc b/tests/specs/Schema/OracleSchemaBuilderSpec.cfc index 5608b674..5841f6f4 100644 --- a/tests/specs/Schema/OracleSchemaBuilderSpec.cfc +++ b/tests/specs/Schema/OracleSchemaBuilderSpec.cfc @@ -47,7 +47,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { function complicatedTable() { return [ - "CREATE TABLE ""USERS"" (""ID"" NUMBER(10, 0) NOT NULL, ""USERNAME"" VARCHAR2(255) NOT NULL, ""FIRST_NAME"" VARCHAR2(255) NOT NULL, ""LAST_NAME"" VARCHAR2(255) NOT NULL, ""PASSWORD"" VARCHAR2(100) NOT NULL, ""COUNTRY_ID"" NUMBER(10, 0) NOT NULL, ""CREATED_DATE"" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL, ""MODIFIED_DATE"" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT ""PK_USERS_ID"" PRIMARY KEY (""ID""), CONSTRAINT ""FK_USERS_COUNTRY_ID"" FOREIGN KEY (""COUNTRY_ID"") REFERENCES ""COUNTRIES"" (""ID"") ON DELETE CASCADE)", + "CREATE TABLE ""USERS"" (""ID"" NUMBER(10, 0) NOT NULL, ""USERNAME"" VARCHAR2(255) NOT NULL, ""FIRST_NAME"" VARCHAR2(255) NOT NULL, ""LAST_NAME"" VARCHAR2(255) NOT NULL, ""PASSWORD"" VARCHAR2(100) NOT NULL, ""COUNTRY_ID"" NUMBER(10, 0) NOT NULL, ""CREATED_DATE"" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, ""MODIFIED_DATE"" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT ""PK_USERS_ID"" PRIMARY KEY (""ID""), CONSTRAINT ""FK_USERS_COUNTRY_ID"" FOREIGN KEY (""COUNTRY_ID"") REFERENCES ""COUNTRIES"" (""ID"") ON DELETE CASCADE)", "CREATE SEQUENCE ""SEQ_USERS""", "CREATE OR REPLACE TRIGGER ""TRG_USERS"" BEFORE INSERT ON ""USERS"" FOR EACH ROW WHEN (NEW.""ID"" IS NULL) BEGIN SELECT ""SEQ_USERS"".NEXTVAL INTO ::NEW.""ID"" FROM dual; END" ]; @@ -106,7 +106,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function datetime() { - return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" DATE NOT NULL)" ]; + return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" TIMESTAMP NOT NULL)" ]; } function datetimeTz() { @@ -230,7 +230,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function nullableTimestamps() { - return [ "CREATE TABLE ""POSTS"" (""CREATEDDATE"" DATE, ""MODIFIEDDATE"" DATE)" ]; + return [ "CREATE TABLE ""POSTS"" (""CREATEDDATE"" TIMESTAMP, ""MODIFIEDDATE"" TIMESTAMP)" ]; } function point() { @@ -242,7 +242,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function softDeletes() { - return [ "CREATE TABLE ""POSTS"" (""DELETEDDATE"" DATE)" ]; + return [ "CREATE TABLE ""POSTS"" (""DELETEDDATE"" TIMESTAMP)" ]; } function softDeletesTz() { @@ -296,7 +296,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function time() { - return [ "CREATE TABLE ""RECURRING_TASKS"" (""FIRE_TIME"" DATE NOT NULL)" ]; + return [ "CREATE TABLE ""RECURRING_TASKS"" (""FIRE_TIME"" TIMESTAMP NOT NULL)" ]; } function timeTz() { @@ -304,12 +304,16 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function timestamp() { - return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" DATE NOT NULL)" ]; + return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" TIMESTAMP NOT NULL)" ]; + } + + function timestampPrecision() { + return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" TIMESTAMP(6) NOT NULL)" ]; } function timestamps() { return [ - "CREATE TABLE ""POSTS"" (""CREATEDDATE"" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL, ""MODIFIEDDATE"" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL)" + "CREATE TABLE ""POSTS"" (""CREATEDDATE"" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, ""MODIFIEDDATE"" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL)" ]; } @@ -407,11 +411,11 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { } function timestampWithCurrent() { - return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL)" ]; + return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL)" ]; } function timestampWithNullable() { - return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" DATE)" ]; + return [ "CREATE TABLE ""POSTS"" (""POSTED_DATE"" TIMESTAMP)" ]; } function nullable() { @@ -497,7 +501,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { function basicIndex() { return [ - "CREATE TABLE ""USERS"" (""PUBLISHED_DATE"" DATE NOT NULL)", + "CREATE TABLE ""USERS"" (""PUBLISHED_DATE"" TIMESTAMP NOT NULL)", "CREATE INDEX ""IDX_USERS_PUBLISHED_DATE"" ON ""USERS"" (""PUBLISHED_DATE"")" ]; } @@ -586,7 +590,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { function modifyMultipleColumns() { return [ "ALTER TABLE ""USERS"" MODIFY (""NAME"" CLOB NOT NULL)", - "ALTER TABLE ""USERS"" MODIFY (""PURCHASED_DATE"" DATE)" + "ALTER TABLE ""USERS"" MODIFY (""PURCHASED_DATE"" TIMESTAMP)" ]; } @@ -610,7 +614,7 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { "ALTER TABLE ""USERS"" DROP COLUMN ""IS_ACTIVE""", "ALTER TABLE ""USERS"" ADD ""TSHIRT_SIZE"" VARCHAR2(255) NOT NULL", "ALTER TABLE ""USERS"" RENAME COLUMN ""NAME"" TO ""USERNAME""", - "ALTER TABLE ""USERS"" MODIFY (""PURCHASE_DATE"" DATE)", + "ALTER TABLE ""USERS"" MODIFY (""PURCHASE_DATE"" TIMESTAMP)", "ALTER TABLE ""USERS"" ADD CONSTRAINT ""UNQ_USERS_USERNAME"" UNIQUE (""USERNAME"")", "ALTER TABLE ""USERS"" ADD CONSTRAINT ""UNQ_USERS_EMAIL"" UNIQUE (""EMAIL"")", "ALTER TABLE ""USERS"" DROP CONSTRAINT ""IDX_USERS_CREATED_DATE""", diff --git a/tests/specs/Schema/PostgresSchemaBuilderSpec.cfc b/tests/specs/Schema/PostgresSchemaBuilderSpec.cfc index 44ebe84e..66da119b 100644 --- a/tests/specs/Schema/PostgresSchemaBuilderSpec.cfc +++ b/tests/specs/Schema/PostgresSchemaBuilderSpec.cfc @@ -253,6 +253,11 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { return [ "CREATE TABLE ""posts"" (""posted_date"" TIMESTAMP NOT NULL)" ]; } + function timestampPrecision() { + return [ "CREATE TABLE ""posts"" (""posted_date"" TIMESTAMP(6) NOT NULL)" ]; + } + + function timestamps() { return [ "CREATE TABLE ""posts"" (""createdDate"" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, ""modifiedDate"" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)" diff --git a/tests/specs/Schema/SQLiteSchemaBuilderSpec.cfc b/tests/specs/Schema/SQLiteSchemaBuilderSpec.cfc index 86d5455e..49e6c4fe 100644 --- a/tests/specs/Schema/SQLiteSchemaBuilderSpec.cfc +++ b/tests/specs/Schema/SQLiteSchemaBuilderSpec.cfc @@ -285,6 +285,10 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { return [ "CREATE TABLE ""posts"" (""posted_date"" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)" ]; } + function timestampPrecision() { + return [ "CREATE TABLE ""posts"" (""posted_date"" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)" ]; + } + function timestampWithNullable() { return [ "CREATE TABLE ""posts"" (""posted_date"" TEXT)" ]; } diff --git a/tests/specs/Schema/SqlServerSchemaBuilderSpec.cfc b/tests/specs/Schema/SqlServerSchemaBuilderSpec.cfc index c403f4ba..d0300c32 100644 --- a/tests/specs/Schema/SqlServerSchemaBuilderSpec.cfc +++ b/tests/specs/Schema/SqlServerSchemaBuilderSpec.cfc @@ -250,6 +250,10 @@ component extends="tests.resources.AbstractSchemaBuilderSpec" { return [ "CREATE TABLE [posts] ([posted_date] DATETIME2 NOT NULL)" ]; } + function timestampPrecision() { + return [ "CREATE TABLE [posts] ([posted_date] DATETIME2(6) NOT NULL)" ]; + } + function timestampTz() { return [ "CREATE TABLE [posts] ([posted_date] DATETIMEOFFSET NOT NULL)" ]; }