lib/schema/tablebuilder.js

TableBuilder

Takes the function passed to the "createTable" or "table/editTable" functions and calls it with the "TableBuilder" as both the context and the first argument. Inside this function we can specify what happens to the method, pushing everything we want to do onto the "allStatements" array,

which is then compiled into sql.

var _ = require('lodash'); function TableBuilder(method, tableName, fn) { this._fn = fn; this._method = method; this._tableName = tableName; this._statements = []; this._single = {}; }

Convert the current tableBuilder object "toSQL" giving us additional methods if we're altering rather than creating the table.

TableBuilder.prototype.toSQL = function() { if (this._method === 'alter') { _.extend(this, AlterMethods); } this._fn.call(this, this); var TableCompiler = this.client.TableCompiler; return new TableCompiler(this).toSQL(); }; var AlterMethods = {

Renames the current column from the current TODO: this.column(from).rename(to)

renameColumn: function(from, to) { this._statements.push({ grouping: 'alterTable', method: 'renameColumn', args: [from, to] }); return this; }, dropTimestamps: function() { return this.dropColumns(['created_at', 'updated_at']); }

TODO: changeType

};

Drop a column from the current table. TODO: Enable this.column(columnName).drop();

AlterMethods.dropColumn = AlterMethods.dropColumns = function() { this._statements.push({ grouping: 'alterTable', method: 'dropColumn', args: _.toArray(arguments) }); return this; }; _.each([

Each of the index methods can be called individually, with the column name to be used, e.g. table.unique('column').

'index', 'primary', 'unique',

Key specific

'dropPrimary', 'dropUnique', 'dropIndex', 'dropForeign' ], function(method) { TableBuilder.prototype[method] = function() { this._statements.push({ grouping: 'alterTable', method: method, args: _.toArray(arguments) }); return this; }; });

Warn if we're not in MySQL, since that's the only time these three are supported.

var specialMethods = ['engine', 'charset', 'collate']; _.each(specialMethods, function(method) { TableBuilder.prototype[method] = function(value) { if (false) { warn('Knex only supports ' + method + ' statement with mysql.'); } if (this.__method === 'alter') { warn('Knex does not support altering the ' + method + ' outside of the create table, please use knex.raw statement.'); } this._single[method] = value; }; });

Each of the column types that we can add, we create a new ColumnBuilder instance and push it onto the statements array.

var columnTypes = [

Numeric

'tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'decimal', 'float', 'double', 'real', 'bit', 'boolean', 'serial',

Date / Time

'date', 'datetime', 'timestamp', 'time', 'year',

String

'char', 'varchar', 'tinytext', 'tinyText', 'text', 'mediumtext', 'mediumText', 'longtext', 'longText', 'binary', 'varbinary', 'tinyblob', 'tinyBlob', 'mediumblob', 'mediumBlob', 'blob', 'longblob', 'longBlob', 'enum', 'set',

Increments, Aliases, and Additional

'bool', 'dateTime', 'increments', 'bigincrements', 'bigIncrements', 'integer', 'biginteger', 'bigInteger', 'string', 'timestamps', 'json', 'uuid', 'enu', 'specificType' ];

For each of the column methods, create a new "ColumnBuilder" interface, push it onto the "allStatements" stack, and then return the interface, with which we can add indexes, etc.

_.each(columnTypes, function(type) { TableBuilder.prototype[type] = function() { var args = _.toArray(arguments);

The "timestamps" call is really a compound call to set the created_at and updated_at columns.

if (type === 'timestamps') { if (args[0] === true) { this.timestamp('created_at'); this.timestamp('updated_at'); } else { this.datetime('created_at'); this.datetime('updated_at'); } return; } var ColumnBuilder = this.client.ColumnBuilder; var builder = new ColumnBuilder(this, type, args); this._statements.push({ grouping: 'columns', builder: builder }); return builder; }; });

Set the comment value for a table, they're only allowed to be called once per table.

TableBuilder.prototype.comment = function(value) { this._single.comment = value; };

Set a foreign key on the table, calling `table.foreign('column_name').references('column').on('table').onDelete()... Also called from the ColumnBuilder context when chaining.

TableBuilder.prototype.foreign = function(column) { var foreignData = {column: column}; this._statements.push({ grouping: 'alterTable', method: 'foreign', args: [foreignData] }); var returnObj = { references: function(tableColumn) { var pieces; if (_.isString(tableColumn)) { pieces = tableColumn.split('.'); } if (!pieces || pieces.length === 1) { foreignData.references = pieces ? pieces[0] : tableColumn; return { on: function(tableName) { foreignData.inTable = tableName; return returnObj; }, inTable: function() { return this.on.apply(this, arguments); } }; } foreignData.inTable = pieces[0]; foreignData.references = pieces[1]; return returnObj; }, onUpdate: function(statement) { foreignData.onUpdate = statement; return returnObj; }, onDelete: function(statement) { foreignData.onDelete = statement; return returnObj; }, _columnBuilder: function(builder) { _.extend(builder, returnObj); returnObj = builder; return builder; } }; return returnObj; }; module.exports = TableBuilder;