Skip to content

Commit dfcd030

Browse files
committed
ColumnDefinition: Make timestamp columns NULL by default
This makes timestamp column definitions consistent with how all the other mysql-plus column definitions work. This also fixes a bug where some timestamp column definitions would cause unnecessary ALTER migrations. Fixes #3
1 parent 48e76e9 commit dfcd030

File tree

10 files changed

+217
-35
lines changed

10 files changed

+217
-35
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,48 @@ Compatible types:
12211221
+ `datetime`
12221222
+ `timestamp`
12231223

1224+
#### TimestampColumnDefinition
1225+
1226+
Compatible types:
1227+
1228+
+ `timestamp`
1229+
1230+
There aren't any extra methods on this type, but there are some things you should be aware of with `timestamp` columns:
1231+
1232+
##### NULL Timestamps
1233+
1234+
Normally, timestamp columns are `NOT NULL` by default, however, mysql-plus defines timestamp columns to be `NULL` by default to keep column definition semantics consistent. Therefore, the following column definition:
1235+
1236+
```js
1237+
{
1238+
ts: ColTypes.timestamp(),
1239+
}
1240+
```
1241+
1242+
would define a column with this SQL:
1243+
1244+
```sql
1245+
`ts` timestamp NULL DEFAULT NULL
1246+
```
1247+
1248+
##### Timestamps' DEFAULT value
1249+
1250+
If you define a timestamp column and use the `notNull()` method, the column's `DEFAULT` value will be set to `CURRENT_TIMESTAMP`. So the following:
1251+
1252+
```js
1253+
{
1254+
ts: ColTypes.timestamp().notNull(), // equivalent to `ColTypes.timestamp().notNull().defaultCurrentTimestamp()`
1255+
}
1256+
```
1257+
1258+
would define a column with this SQL:
1259+
1260+
```sql
1261+
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
1262+
```
1263+
1264+
Normally if the `DEFAULT` is unspecified, MySQL uses `CURRENT_TIMESTAMP` as the `DEFAULT` value of only the first timestamp column and `'0000-00-00 00:00:00'` for subsequent columns, but `mysql-plus` uses `CURRENT_TIMESTAMP` for all timestamp columns for consistency.
1265+
12241266
#### GeometricalColumnDefinition
12251267

12261268
Methods:

jsdoc2md/README.hbs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,48 @@ Compatible types:
428428
+ `datetime`
429429
+ `timestamp`
430430

431+
#### TimestampColumnDefinition
432+
433+
Compatible types:
434+
435+
+ `timestamp`
436+
437+
There aren't any extra methods on this type, but there are some things you should be aware of with `timestamp` columns:
438+
439+
##### NULL Timestamps
440+
441+
Normally, timestamp columns are `NOT NULL` by default, however, mysql-plus defines timestamp columns to be `NULL` by default to keep column definition semantics consistent. Therefore, the following column definition:
442+
443+
```js
444+
{
445+
ts: ColTypes.timestamp(),
446+
}
447+
```
448+
449+
would define a column with this SQL:
450+
451+
```sql
452+
`ts` timestamp NULL DEFAULT NULL
453+
```
454+
455+
##### Timestamps' DEFAULT value
456+
457+
If you define a timestamp column and use the `notNull()` method, the column's `DEFAULT` value will be set to `CURRENT_TIMESTAMP`. So the following:
458+
459+
```js
460+
{
461+
ts: ColTypes.timestamp().notNull(), // equivalent to `ColTypes.timestamp().notNull().defaultCurrentTimestamp()`
462+
}
463+
```
464+
465+
would define a column with this SQL:
466+
467+
```sql
468+
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
469+
```
470+
471+
Normally if the `DEFAULT` is unspecified, MySQL uses `CURRENT_TIMESTAMP` as the `DEFAULT` value of only the first timestamp column and `'0000-00-00 00:00:00'` for subsequent columns, but `mysql-plus` uses `CURRENT_TIMESTAMP` for all timestamp columns for consistency.
472+
431473
#### GeometricalColumnDefinition
432474

433475
Methods:

lib/ColumnDefinitions/ColumnDefinition.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ class ColumnDefinition {
1818
this._baseType = type;
1919
if (m == null) {
2020
this.$hasLength = false;
21+
this._type = type;
2122
} else {
2223
this.$hasLength = true;
23-
type += '(' + mysql.escape(m) + (d == null ? '' : ',' + mysql.escape(d)) + ')';
24+
this._type = type + '(' + mysql.escape(m) + (d == null ? '' : ',' + mysql.escape(d)) + ')';
2425
}
25-
this.$type = type;
2626
this.$primaryKey = false;
2727
this.$unique = false;
2828
this.$index = false;
@@ -68,12 +68,12 @@ class ColumnDefinition {
6868
}
6969

7070
$equals(columnDefinition) {
71-
var thisType = this.$type;
72-
var otherType = columnDefinition.$type;
71+
var thisType = this._type;
72+
var otherType = columnDefinition._type;
7373

7474
if (this.$hasLength && columnDefinition.$hasLength) {
75-
thisType = this.$type;
76-
otherType = columnDefinition.$type;
75+
thisType = this._type;
76+
otherType = columnDefinition._type;
7777
} else {
7878
thisType = this._baseType;
7979
otherType = columnDefinition._baseType;
@@ -96,7 +96,7 @@ class ColumnDefinition {
9696
}
9797

9898
$toSQL() {
99-
var sql = this.__getType();
99+
var sql = this._type + this.__getExtendedType();
100100

101101
if (this._notNull) {
102102
sql += ' NOT NULL';
@@ -108,8 +108,8 @@ class ColumnDefinition {
108108
return sql;
109109
}
110110

111-
__getType() {
112-
return this.$type;
111+
__getExtendedType() {
112+
return '';
113113
}
114114
}
115115

lib/ColumnDefinitions/NumericColumnDefinition.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ class NumericColumnDefinition extends ColumnDefinition {
3333
this._autoIncrement === columnDefinition._autoIncrement;
3434
}
3535

36-
__getType() {
37-
var type = this.$type;
36+
__getExtendedType() {
37+
var extendedType = '';
3838
if (this._unsigned) {
39-
type += ' unsigned';
39+
extendedType += ' unsigned';
4040
}
4141
if (this._zerofill) {
42-
type += ' zerofill';
42+
extendedType += ' zerofill';
4343
}
4444
if (this._autoIncrement) {
45-
type += ' AUTO_INCREMENT';
45+
extendedType += ' AUTO_INCREMENT';
4646
}
47-
return type;
47+
return extendedType;
4848
}
4949
}
5050

lib/ColumnDefinitions/TextColumnDefinition.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ class TextColumnDefinition extends ColumnDefinition {
4848
return true;
4949
}
5050

51-
__getType() {
52-
var type = this.$type;
51+
__getExtendedType() {
52+
var extendedType = '';
5353
if (this._charset) {
54-
type += ' CHARACTER SET ' + this._charset;
54+
extendedType += ' CHARACTER SET ' + this._charset;
5555
}
5656
if (this._collate) {
57-
type += ' COLLATE ' + this._collate;
57+
extendedType += ' COLLATE ' + this._collate;
5858
}
59-
return type;
59+
return extendedType;
6060
}
6161
}
6262

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
const UpdatableTimeColumnDefinition = require('./UpdatableTimeColumnDefinition');
4+
5+
class TimestampColumnDefinition extends UpdatableTimeColumnDefinition {
6+
constructor(m) {
7+
super('timestamp', m);
8+
this._allowNull = true;
9+
}
10+
11+
notNull() {
12+
this._allowNull = false;
13+
return super.notNull().defaultCurrentTimestamp();
14+
}
15+
16+
default(value) {
17+
if (value === 0) {
18+
value = '0000-00-00 00:00:00';
19+
}
20+
return super.default(value);
21+
}
22+
23+
$equals(columnDefinition) {
24+
return super.$equals(columnDefinition) &&
25+
this._allowNull === columnDefinition._allowNull;
26+
}
27+
28+
__getExtendedType() {
29+
const extendedType = this._allowNull ? ' NULL' : '';
30+
return extendedType + super.__getExtendedType();
31+
}
32+
}
33+
34+
module.exports = TimestampColumnDefinition;

lib/ColumnDefinitions/UpdatableTimeColumnDefinition.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,8 @@ class UpdatableTimeColumnDefinition extends ColumnDefinition {
2222
this._onUpdateCurrentTimestamp === columnDefinition._onUpdateCurrentTimestamp;
2323
}
2424

25-
__getType() {
26-
var type = this.$type;
27-
if (this._onUpdateCurrentTimestamp) {
28-
type += ' ON UPDATE CURRENT_TIMESTAMP';
29-
}
30-
return type;
25+
__getExtendedType() {
26+
return this._onUpdateCurrentTimestamp ? ' ON UPDATE CURRENT_TIMESTAMP' : '';
3127
}
3228
}
3329

lib/ColumnDefinitions/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const ColumnDefinition = require('./ColumnDefinition');
1010
const GeometricalColumnDefinition = require('./GeometricalColumnDefinition');
1111
const NumericColumnDefinition = require('./NumericColumnDefinition');
1212
const TextColumnDefinition = require('./TextColumnDefinition');
13+
const TimestampColumnDefinition = require('./TimestampColumnDefinition');
1314
const UpdatableTimeColumnDefinition = require('./UpdatableTimeColumnDefinition');
1415

1516
const ColumnDefinitions = {
@@ -65,7 +66,7 @@ const ColumnDefinitions = {
6566
return new UpdatableTimeColumnDefinition('datetime', m);
6667
},
6768
timestamp(m) {
68-
return new UpdatableTimeColumnDefinition('timestamp', m);
69+
return new TimestampColumnDefinition(m);
6970
},
7071
time(m) {
7172
return new ColumnDefinition('time', m);

test/integration/MySQLPlus.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,29 @@ describe('MySQLPlus', function() {
526526
' `e` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL\n' +
527527
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci';
528528

529+
const timestampTableName = 'timestamp_table';
530+
const timestampTableSchema = {
531+
columns: {
532+
a: ColTypes.timestamp().notNull(),
533+
b: ColTypes.timestamp().notNull().defaultCurrentTimestamp(),
534+
c: ColTypes.timestamp().notNull().default(0),
535+
d: ColTypes.timestamp(),
536+
e: ColTypes.timestamp().default(null),
537+
f: ColTypes.timestamp().default(0),
538+
g: ColTypes.timestamp().default('2017-03-25 12:46:05'),
539+
},
540+
};
541+
const timestampTableExpectedSQL =
542+
'CREATE TABLE `timestamp_table` (\n' +
543+
' `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n' +
544+
' `b` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n' +
545+
" `c` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n" +
546+
' `d` timestamp NULL DEFAULT NULL,\n' +
547+
' `e` timestamp NULL DEFAULT NULL,\n' +
548+
" `f` timestamp NULL DEFAULT '0000-00-00 00:00:00',\n" +
549+
" `g` timestamp NULL DEFAULT '2017-03-25 12:46:05'\n" +
550+
') ENGINE=InnoDB DEFAULT CHARSET=utf8';
551+
529552

530553
describe('when creating new tables', () => {
531554

@@ -543,6 +566,7 @@ describe('MySQLPlus', function() {
543566
pool.defineTable(foreignKeysTableName, foreignKeysTableSchema);
544567
pool.defineTable(optionsTableName, optionsTableSchema);
545568
pool.defineTable(textTableName, textTableSchema);
569+
pool.defineTable(timestampTableName, timestampTableSchema);
546570
pool.sync(done);
547571
});
548572

@@ -629,6 +653,13 @@ describe('MySQLPlus', function() {
629653
result[0]['Create Table'].should.equal(textTableExpectedSQL);
630654
cb10();
631655
});
656+
657+
const cbTimestamp = cbManager.registerCallback();
658+
pool.query(`SHOW CREATE TABLE \`${timestampTableName}\``, (err, result) => {
659+
if (err) throw err;
660+
result[0]['Create Table'].should.equal(timestampTableExpectedSQL);
661+
cbTimestamp();
662+
});
632663
});
633664

634665
});
@@ -651,6 +682,7 @@ describe('MySQLPlus', function() {
651682
pool.defineTable(foreignKeysTableName, foreignKeysTableSchema);
652683
pool.defineTable(optionsTableName, optionsTableSchema);
653684
pool.defineTable(textTableName, textTableSchema);
685+
pool.defineTable(timestampTableName, timestampTableSchema);
654686
pool.sync(done);
655687
});
656688

@@ -742,6 +774,13 @@ describe('MySQLPlus', function() {
742774
result[0]['Create Table'].should.equal(textTableExpectedSQL);
743775
cb10();
744776
});
777+
778+
const cbTimestamp = cbManager.registerCallback();
779+
pool.query(`SHOW CREATE TABLE \`${timestampTableName}\``, (err, result) => {
780+
if (err) throw err;
781+
result[0]['Create Table'].should.equal(timestampTableExpectedSQL);
782+
cbTimestamp();
783+
});
745784
});
746785

747786
});
@@ -764,6 +803,7 @@ describe('MySQLPlus', function() {
764803
pool.defineTable(foreignKeysTableName, foreignKeysTableSchema);
765804
pool.defineTable(optionsTableName, optionsTableSchema);
766805
pool.defineTable(textTableName, textTableSchema);
806+
pool.defineTable(timestampTableName, timestampTableSchema);
767807
pool.sync(done);
768808
});
769809

@@ -850,6 +890,13 @@ describe('MySQLPlus', function() {
850890
result[0]['Create Table'].should.equal(textTableExpectedSQL);
851891
cb10();
852892
});
893+
894+
const cbTimestamp = cbManager.registerCallback();
895+
pool.query(`SHOW CREATE TABLE \`${timestampTableName}\``, (err, result) => {
896+
if (err) throw err;
897+
result[0]['Create Table'].should.equal(timestampTableExpectedSQL);
898+
cbTimestamp();
899+
});
853900
});
854901

855902
});

0 commit comments

Comments
 (0)