diff --git a/index.js b/index.js index a879ff0..50ea1ab 100644 --- a/index.js +++ b/index.js @@ -56,20 +56,21 @@ var Sqlite3Driver = Base.extend({ callback(null); }, - createColumnDef: function(name, spec, options) { - name = '"' + name + '"'; + createColumnDef: function(name, spec, options, tableName) { + var quotedName = '"' + name + '"'; var dType = this.mapDataType(spec.type); var len = spec.length ? util.format('(%s)', spec.length) : ''; - var constraint = this.createColumnConstraint(spec, options); + var constraint = this.createColumnConstraint(spec, options, tableName, name); if(spec.type === type.INTEGER) len = ''; return { foreignKey: null, - constraints: [name, dType, len, constraint].join(' ') }; + constraints: [quotedName, dType, len, constraint].join(' ') }; }, - createColumnConstraint: function(spec, options) { + createColumnConstraint: function(spec, options, tableName, columnName) { + var cb; var constraint = []; if (spec.primaryKey && options.emitPrimaryKey) { constraint.push('PRIMARY KEY'); @@ -95,6 +96,22 @@ var Sqlite3Driver = Base.extend({ constraint.push(spec.defaultValue); } + if (spec.foreignKey) { + constraint.push(`REFERENCES ${spec.foreignKey.table}(${spec.foreignKey.mapping})`) + if( spec.foreignKey.rules ){ + Object.keys(spec.foreignKey.rules) + .forEach( (rule) => { + switch(rule){ + case 'onDelete': constraint.push(`ON DELETE ${spec.foreignKey.rules[rule]}`) + break + case 'onUpdate': constraint.push(`ON UPDATE ${spec.foreignKey.rules[rule]}`) + break + default: throw new Error('Unsupported foreign key action trigger: ' + rule ) + } + }) + } + } + return constraint.join(' '); }, diff --git a/test/foreign_key_batch.js b/test/foreign_key_batch.js new file mode 100644 index 0000000..2df6473 --- /dev/null +++ b/test/foreign_key_batch.js @@ -0,0 +1,77 @@ +const dbmeta = require('db-meta') +const dataType = require('db-migrate-shared').dataType +const fs = require('fs') +const assert = require('assert') + +module.exports = (driver, config, internals) => ({ + 'columnForeignKeySpec': { + topic: function () { + driver.connect(config, internals, function (err, db) { + db.createTable('event_type', { + + id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }, + title: { type: dataType.STRING } + }, function(err) { + if (err) { + return this.callback(err); + } + db.createTable('event', { + id: { + type: dataType.INTEGER, + primaryKey: true, + autoIncrement: true + }, + event_type_id: { + type: dataType.INTEGER, + notNull: true, + foreignKey: { + name: 'fk_event_event_type', + table: 'event_type', + mapping: 'id', + rules: { + onDelete: 'CASCADE' + }, + } }, + title: { + type: dataType.STRING + } + }, this.callback.bind(this, null, db)); + }.bind(this)); + }.bind(this)) + }, + + teardown: function(db) { + db.close(function (err) { + fs.unlink(config.filename, this.callback); + }.bind(this)); + }, + + 'sets usage and constraints': { + topic: function (db) { + dbmeta('sqlite3', {connection: db.connection}, function (err, meta) { + if (err) { + return this.callback(err); + } + meta.getTables( (err, tables) => { + if (err) { + return this.callback(err) + } + this.callback( undefined, tables.find( (table) => table.getName() == "event" ) ) + }) + }.bind(this)); + }, + 'that has foreign key column with the expected reference': function (err, table) { + const foreignKeyDefinition = table.meta + .sql + .match(/"event_type_id"[^,]+/)[0] + .replace(/\s{2,}/g,' ') + + assert.equal( + foreignKeyDefinition, + '"event_type_id" INTEGER NOT NULL REFERENCES event_type(id) ON DELETE CASCADE' + ) + } + + } + } +}) diff --git a/test/sqlite3_test.js b/test/sqlite3_test.js index 937f584..ce562a2 100644 --- a/test/sqlite3_test.js +++ b/test/sqlite3_test.js @@ -8,6 +8,8 @@ var driver = require('../'); var config = require('./db.config.json').sqlite3; +var foreignKeyBatch = require('./foreign_key_batch') + var internals = {}; internals.mod = { log: log, @@ -38,7 +40,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has resulting table metadata': { @@ -138,7 +140,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has table metadata': { @@ -172,7 +174,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has table metadata': { @@ -208,7 +210,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has column metadata': { @@ -249,7 +251,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has resulting index metadata': { @@ -287,7 +289,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'with additional row': function (db) { @@ -312,7 +314,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'with additional row': function (db) { @@ -339,7 +341,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has resulting index metadata': { @@ -376,7 +378,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has resulting index metadata': { @@ -406,7 +408,7 @@ vows.describe('sqlite3').addBatch({ teardown: function (db) { db.close(function (err) { fs.unlink(config.filename, this.callback); - }); + }.bind(this)); }, 'has migrations table': { @@ -452,7 +454,8 @@ vows.describe('sqlite3').addBatch({ } } } - }).export(module); +}).addBatch( foreignKeyBatch(driver, config, internals) ) + .export(module); function findByName(columns, name) { for (var i = 0; i < columns.length; i++) {