diff --git a/index.js b/index.js index 78697bdd..c8f23922 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,7 @@ const connect = async function connect(opts) { return realm; }; -Object.assign(Realm.prototype, migrations, { DataTypes }); +Object.assign(Realm.prototype, migrations); Object.assign(Realm, { connect, Bone, diff --git a/src/data_types.js b/src/data_types.js index ca10063a..b3ecef60 100644 --- a/src/data_types.js +++ b/src/data_types.js @@ -14,7 +14,13 @@ const Raw = require('./raw'); class DataType { static findType(columnType) { - const { STRING, TEXT, DATE, DATEONLY, INTEGER, BIGINT, BOOLEAN, BINARY, VARBINARY, BLOB } = this; + const { + STRING, TEXT, + DATE, DATEONLY, + TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, + BOOLEAN, + BINARY, VARBINARY, BLOB, + } = this; const [ , dataType, appendix ] = columnType.match(/(\w+)(?:\((\d+)\))?/); const length = appendix && parseInt(appendix, 10); @@ -39,10 +45,13 @@ class DataType { case 'int': case 'integer': case 'numeric': + return new INTEGER(length); case 'mediumint': + return new MEDIUMINT(length); case 'smallint': + return new SMALLINT(length); case 'tinyint': - return new INTEGER(length); + return new TINYINT(length); case 'bigint': return new BIGINT(length); case 'boolean': @@ -203,12 +212,58 @@ class INTEGER extends DataType { } } +/** + * 8 bit integer + * @example + * TINYINT + * TINYINT.UNSIGNED + * TINYINT(1) + * @param {number} length + */ +class TINYINT extends INTEGER { + constructor(length) { + super(length); + this.dataType = 'tinyint'; + } +} + +/** + * 16 bit integer + * @example + * SMALLINT + * SMALLINT.UNSIGNED + * SMALLINT(2) + * @param {number} length + */ +class SMALLINT extends INTEGER { + constructor(length) { + super(length); + this.dataType = 'smallint'; + } +} + +/** + * 24 bit integer + * @example + * MEDIUMINT + * MEDIUMINT.UNSIGNED + * MEDIUMINT(3) + * @param {number} length + */ +class MEDIUMINT extends INTEGER { + constructor(length) { + super(length); + this.dataType = 'mediumint'; + } +} + + /** * 64 bit integer * @example * BIGINT * BIGINT.UNSIGNED - * BIGINT(10) + * BIGINT(8) * @param {number} length */ class BIGINT extends INTEGER { @@ -422,6 +477,9 @@ class JSONB extends JSON { const DataTypes = { STRING, + TINYINT, + SMALLINT, + MEDIUMINT, INTEGER, BIGINT, DATE, diff --git a/src/drivers/abstract/attribute.js b/src/drivers/abstract/attribute.js index 18fde7fe..18dd95b8 100644 --- a/src/drivers/abstract/attribute.js +++ b/src/drivers/abstract/attribute.js @@ -65,6 +65,14 @@ function createType(DataTypes, params) { switch (type.constructor.name) { case 'DATE': return new DataType(type.precision, type.timezone); + case 'TINYINT': + case 'SMALLINT': + case 'MEDIUMINT': + case 'INTEGER': + case 'BIGINT': + case 'BINARY': + case 'VARBINARY': + return new DataType(type.length); default: return new DataType(); } diff --git a/src/drivers/postgres/data_types.js b/src/drivers/postgres/data_types.js index 19dd6e7e..9d9beec0 100644 --- a/src/drivers/postgres/data_types.js +++ b/src/drivers/postgres/data_types.js @@ -61,12 +61,22 @@ class Postgres_BIGINT extends Postgres_INTEGER { } } +class Postgres_SMALLINT extends Postgres_INTEGER { + constructor() { + super(); + this.dataType = 'smallint'; + } +} + class Postgres_DataTypes extends DataTypes { static DATE = Postgres_DATE; static JSONB = Postgres_JSONB; static BINARY = Postgres_BINARY; static VARBINARY = Postgres_BINARY; static BLOB = Postgres_BINARY; + static TINYINT = Postgres_SMALLINT; + static SMALLINT = Postgres_SMALLINT; + static MEDIUMINT = Postgres_INTEGER; static INTEGER = Postgres_INTEGER; static BIGINT = Postgres_BIGINT; } diff --git a/src/realm.js b/src/realm.js index d32df32c..809fc289 100644 --- a/src/realm.js +++ b/src/realm.js @@ -129,6 +129,11 @@ class Realm { this.options = Spine.options = options; } + get DataTypes() { + if (!this.driver) throw new Error('database not connected yet'); + return this.driver.DataTypes; + } + define(name, attributes, opts = {}, descriptors = {}) { const Model = class extends this.Bone { static name = name; diff --git a/test/unit/bone.test.js b/test/unit/bone.test.js index a2a7b2ef..639b94a7 100644 --- a/test/unit/bone.test.js +++ b/test/unit/bone.test.js @@ -4,7 +4,11 @@ const assert = require('assert').strict; const { Bone, DataTypes, connect } = require('../..'); const expect = require('expect.js'); -const { BIGINT, STRING, DATE } = DataTypes; +const { + TINYINT, MEDIUMINT, BIGINT, + STRING, + DATE, +} = DataTypes; describe('=> Bone', function() { before(async function() { @@ -312,4 +316,20 @@ describe('=> Bone', function() { expect(note.updatedAt).to.be.a(Date); }); }); + + describe('=> Bone.sync()', function() { + it('should allow specific types such as TINYINT', async function() { + class Note extends Bone { + static attributes = { + isPrivate: TINYINT(1), + wordCount: MEDIUMINT, + } + } + await Note.sync({ force: true }); + const result = await Note.describe(); + assert.equal(result.is_private.columnType, 'tinyint(1)'); + // MySQL 5.x returns column type with length regardless specified or not + assert.ok(result.word_count.columnType.startsWith('mediumint')); + }); + }); }); diff --git a/test/unit/data_types.test.js b/test/unit/data_types.test.js index 73bc5d93..e08d3b76 100644 --- a/test/unit/data_types.test.js +++ b/test/unit/data_types.test.js @@ -7,11 +7,14 @@ const Raw = require('../../src/raw'); const Postgres_DataTypes = require('../../src/drivers/postgres/data_types'); const SQLite_DataTypes = require('../../src/drivers/sqlite/data_types'); - - describe('=> Data Types', () => { const { - STRING, BOOLEAN, DATE, DATEONLY, INTEGER, BIGINT, TEXT, JSON, JSONB, BLOB, BINARY, VARBINARY, + STRING, TEXT, + BOOLEAN, + DATE, DATEONLY, + TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, + JSON, JSONB, + BLOB, BINARY, VARBINARY, } = DataTypes; it('STRING', () => { @@ -41,6 +44,27 @@ describe('=> Data Types', () => { assert.equal(new DATEONLY().toSqlString(), 'DATE'); }); + it('TINYINT', () => { + assert.equal(new TINYINT().dataType, 'tinyint'); + assert.equal(new TINYINT(1).toSqlString(), 'TINYINT(1)'); + assert.equal(new TINYINT().UNSIGNED.toSqlString(), 'TINYINT UNSIGNED'); + assert.equal(new TINYINT().UNSIGNED.ZEROFILL.toSqlString(), 'TINYINT UNSIGNED ZEROFILL'); + }); + + it('SMALLINT', () => { + assert.equal(new SMALLINT().dataType, 'smallint'); + assert.equal(new SMALLINT(1).toSqlString(), 'SMALLINT(1)'); + assert.equal(new SMALLINT().UNSIGNED.toSqlString(), 'SMALLINT UNSIGNED'); + assert.equal(new SMALLINT().UNSIGNED.ZEROFILL.toSqlString(), 'SMALLINT UNSIGNED ZEROFILL'); + }); + + it('MEDIUMINT', () => { + assert.equal(new MEDIUMINT().dataType, 'mediumint'); + assert.equal(new MEDIUMINT(1).toSqlString(), 'MEDIUMINT(1)'); + assert.equal(new MEDIUMINT().UNSIGNED.toSqlString(), 'MEDIUMINT UNSIGNED'); + assert.equal(new MEDIUMINT().UNSIGNED.ZEROFILL.toSqlString(), 'MEDIUMINT UNSIGNED ZEROFILL'); + }); + it('INTEGER', () => { assert.equal(new INTEGER().dataType, 'integer'); assert.equal(new INTEGER(10).toSqlString(), 'INTEGER(10)'); @@ -103,7 +127,7 @@ describe('=> DataTypes type casting', function() { await assert.rejects(async () => { new INTEGER().uncast('yes?'); }, /Error: invalid integer: yes?/); - + assert.equal(new INTEGER().uncast('yes?', false), 'yes?'); }); @@ -240,6 +264,15 @@ describe('=> DataTypes.findType()', () => { assert.equal(DataTypes.findType('mediumblob').toSqlString(), 'MEDIUMBLOB'); }); + it('integer => INTEGER', () => { + const { TINYINT, SMALLINT, MEDIUMINT, INTEGER } = DataTypes; + assert.ok(DataTypes.findType('tinyint') instanceof TINYINT); + assert.ok(DataTypes.findType('smallint') instanceof SMALLINT); + assert.ok(DataTypes.findType('mediumint') instanceof MEDIUMINT); + assert.ok(DataTypes.findType('integer') instanceof INTEGER); + assert.equal(DataTypes.findType('bigint').toSqlString(), 'BIGINT'); + }); + it('unknown type', async () => { await assert.rejects(async () => DataTypes.findType('error'), /Unexpected data type error/); }); diff --git a/test/unit/drivers/postgres/data_types.test.js b/test/unit/drivers/postgres/data_types.test.js index dff55e46..20169365 100644 --- a/test/unit/drivers/postgres/data_types.test.js +++ b/test/unit/drivers/postgres/data_types.test.js @@ -5,7 +5,11 @@ const DataTypes = require('../../../../src/drivers/postgres/data_types'); describe('=> Data Types', () => { const { - STRING, DATE, JSONB, VARBINARY, BINARY, BLOB + STRING, + DATE, + JSONB, + VARBINARY, BINARY, BLOB, + TINYINT, SMALLINT, MEDIUMINT, INTEGER, } = DataTypes; it('STRING', () => { @@ -35,5 +39,12 @@ describe('=> Data Types', () => { assert.equal(new JSONB().dataType, 'jsonb'); assert.equal(new JSONB().toSqlString(), 'JSONB'); }); + + it('INTEGER', () => { + assert.equal(new TINYINT().toSqlString(), 'SMALLINT'); + assert.equal(new SMALLINT().toSqlString(), 'SMALLINT'); + assert.equal(new MEDIUMINT().toSqlString(), 'INTEGER'); + assert.equal(new INTEGER().toSqlString(), 'INTEGER'); + }); });