mirror of
https://github.com/knex/knex.git
synced 2025-12-27 06:58:39 +00:00
beginning to properly modularize things
This commit is contained in:
parent
069b4e56d0
commit
10940cd0c3
13
.jshintrc
Normal file
13
.jshintrc
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"boss": true,
|
||||
"browser": true,
|
||||
"expr": true,
|
||||
"eqnull": true,
|
||||
"latedef": true,
|
||||
"newcap": false,
|
||||
"node": true,
|
||||
"strict": false,
|
||||
"supernew": true,
|
||||
"sub": true,
|
||||
"trailing": true
|
||||
}
|
||||
@ -105,97 +105,4 @@ exports.protoProps = {
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.grammar = {};
|
||||
|
||||
exports.schemaGrammar = {
|
||||
|
||||
// Get the primary key command if it exists on the blueprint.
|
||||
getCommandByName: function(blueprint, name) {
|
||||
var commands = this.getCommandsByName(blueprint, name);
|
||||
if (commands.length > 0) return commands[0];
|
||||
},
|
||||
|
||||
// Get all of the commands with a given name.
|
||||
getCommandsByName: function(blueprint, name) {
|
||||
return _.filter(blueprint.commands, function(value) { return value.name == name; }) || [];
|
||||
},
|
||||
|
||||
// Used to compile any database specific items.
|
||||
compileAdditional: function() {},
|
||||
|
||||
// Compile a create table command.
|
||||
compileCreateTable: function(blueprint) {
|
||||
var columns = this.getColumns(blueprint).join(', ');
|
||||
return 'create table ' + this.wrapTable(blueprint) + ' (' + columns + ')';
|
||||
},
|
||||
|
||||
// Compile a drop table command.
|
||||
compileDropTable: function(blueprint) {
|
||||
return 'drop table ' + this.wrapTable(blueprint);
|
||||
},
|
||||
|
||||
// Compile a drop table (if exists) command.
|
||||
compileDropTableIfExists: function(blueprint) {
|
||||
return 'drop table if exists ' + this.wrapTable(blueprint);
|
||||
},
|
||||
|
||||
// Compile a drop index command.
|
||||
compileDropIndex: function(blueprint, command) {
|
||||
return 'drop index ' + command.index;
|
||||
},
|
||||
|
||||
// Create the column definition for a string type.
|
||||
typeString: function(column) {
|
||||
return "varchar(" + column.length + ")";
|
||||
},
|
||||
|
||||
// Create the column definition for a text type.
|
||||
typeText: function() {
|
||||
return 'text';
|
||||
},
|
||||
|
||||
// Create the column definition for a tiny integer type.
|
||||
typeTinyInteger: function() {
|
||||
return 'tinyint';
|
||||
},
|
||||
|
||||
// Create the column definition for a time type.
|
||||
typeTime: function() {
|
||||
return 'time';
|
||||
},
|
||||
|
||||
// Create the column definition for a date type.
|
||||
typeDate: function() {
|
||||
return 'date';
|
||||
},
|
||||
|
||||
// Create the column definition for a binary type.
|
||||
typeBinary: function() {
|
||||
return 'blob';
|
||||
},
|
||||
|
||||
// Create the column definition for a json type.
|
||||
typeJson: function() {
|
||||
return 'text';
|
||||
},
|
||||
|
||||
// Create the column definition for a uuid type.
|
||||
typeUuid: function() {
|
||||
return 'char(36)';
|
||||
},
|
||||
|
||||
// Get the SQL for a nullable column modifier.
|
||||
modifyNullable: function(blueprint, column) {
|
||||
return column.isNullable ? ' null' : ' not null';
|
||||
},
|
||||
|
||||
// Get the SQL for a default column modifier.
|
||||
modifyDefault: function(blueprint, column) {
|
||||
if (column.defaultValue != void 0) {
|
||||
return " default '" + this.getDefaultValue(column.defaultValue) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
307
clients/base/grammar.js
Normal file
307
clients/base/grammar.js
Normal file
@ -0,0 +1,307 @@
|
||||
(function(define) {
|
||||
|
||||
"use strict";
|
||||
|
||||
define(function(require, exports) {
|
||||
|
||||
var _ = require('underscore');
|
||||
|
||||
var Raw = require('../../lib/raw').Raw;
|
||||
var Helpers = require('../../lib/helpers').Helpers;
|
||||
|
||||
// Grammar
|
||||
// -------
|
||||
|
||||
// The list of different components
|
||||
var components = [
|
||||
'aggregate', 'columns', 'from',
|
||||
'joins', 'wheres', 'groups', 'havings',
|
||||
'orders', 'limit', 'offset', 'unions'
|
||||
];
|
||||
|
||||
exports.Grammar = {
|
||||
|
||||
// Compiles the `select` statement, or nested sub-selects
|
||||
// by calling each of the component compilers, trimming out
|
||||
// the empties, and returning a generated query string.
|
||||
compileSelect: function(qb) {
|
||||
var sql = {};
|
||||
if (_.isEmpty(qb.columns)) qb.columns = ['*'];
|
||||
for (var i = 0, l = components.length; i < l; i++) {
|
||||
var component = components[i];
|
||||
var result = _.result(qb, component);
|
||||
if (result != null) {
|
||||
sql[component] = this['compile' + Helpers.capitalize(component)](qb, result);
|
||||
}
|
||||
}
|
||||
return _.compact(sql).join(' ');
|
||||
},
|
||||
|
||||
// Compiles an aggregate query.
|
||||
compileAggregate: function(qb) {
|
||||
var column = this.columnize(qb.aggregate.columns);
|
||||
if (qb.isDistinct && column !== '*') {
|
||||
column = 'distinct ' + column;
|
||||
}
|
||||
return 'select ' + qb.aggregate.type + '(' + column + ') as aggregate';
|
||||
},
|
||||
|
||||
// Compiles the columns in the query, specifying if an item was distinct.
|
||||
compileColumns: function(qb, columns) {
|
||||
if (qb.aggregate != null) return;
|
||||
return (qb.isDistinct ? 'select distinct ' : 'select ') + this.columnize(columns);
|
||||
},
|
||||
|
||||
// Compiles the `from` tableName portion of the query.
|
||||
compileFrom: function(qb, table) {
|
||||
return 'from ' + this.wrapTable(table);
|
||||
},
|
||||
|
||||
// Compiles all each of the `join` clauses on the query,
|
||||
// including any nested join queries.
|
||||
compileJoins: function(qb, joins) {
|
||||
var sql = [];
|
||||
for (var i = 0, l = joins.length; i < l; i++) {
|
||||
var join = joins[i];
|
||||
var clauses = [];
|
||||
for (var i2 = 0, l2 = join.clauses.length; i2 < l2; i2++) {
|
||||
var clause = join.clauses[i2];
|
||||
clauses.push(
|
||||
[clause['bool'], this.wrap(clause['first']), clause.operator, this.wrap(clause['second'])].join(' ')
|
||||
);
|
||||
}
|
||||
clauses[0] = clauses[0].replace(/and |or /, '');
|
||||
sql.push(join.type + ' join ' + this.wrapTable(join.table) + ' on ' + clauses.join(' '));
|
||||
}
|
||||
return sql.join(' ');
|
||||
},
|
||||
|
||||
// Compiles all `where` statements on the query.
|
||||
compileWheres: function(qb) {
|
||||
var sql = [];
|
||||
var wheres = qb.wheres;
|
||||
if (wheres.length === 0) return '';
|
||||
for (var i = 0, l = wheres.length; i < l; i++) {
|
||||
var where = wheres[i];
|
||||
sql.push(where.bool + ' ' + this['where' + where.type](qb, where));
|
||||
}
|
||||
return (sql.length > 0 ? 'where ' + sql.join(' ').replace(/and |or /, '') : '');
|
||||
},
|
||||
|
||||
// Compile the "union" queries attached to the main query.
|
||||
compileUnions: function(qb) {
|
||||
var sql = '';
|
||||
for (var i = 0, l = qb.unions.length; i < l; i++) {
|
||||
var union = qb.unions[i];
|
||||
sql += (union.all ? 'union all ' : 'union ') + this.compileSelect(union.query);
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Compiles a nested where clause.
|
||||
whereNested: function(qb, where) {
|
||||
return '(' + this.compileWheres(where.query).slice(6) + ')';
|
||||
},
|
||||
|
||||
// Compiles a nested where clause.
|
||||
whereSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' ' + where.operator + ' (' + (this.compileSelect(where.query)) + ')';
|
||||
},
|
||||
|
||||
// Compiles a basic where clause.
|
||||
whereBasic: function(qb, where) {
|
||||
return this.wrap(where.column) + ' ' + where.operator + ' ' + this.parameter(where.value);
|
||||
},
|
||||
|
||||
// Compiles a basic exists clause.
|
||||
whereExists: function(qb, where) {
|
||||
return 'exists (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a basic not exists clause.
|
||||
whereNotExists: function(qb, where) {
|
||||
return 'not exists (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a where in clause.
|
||||
whereIn: function(qb, where) {
|
||||
return this.wrap(where.column) + ' in (' + this.parameterize(where.value) + ')';
|
||||
},
|
||||
|
||||
// Compiles a where not in clause.
|
||||
whereNotIn: function(qb, where) {
|
||||
return this.wrap(where.column) + ' not in (' + this.parameterize(where.value) + ')';
|
||||
},
|
||||
|
||||
// Compiles a sub-where in clause.
|
||||
whereInSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' in (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a sub-where not in clause.
|
||||
whereNotInSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' not in (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Where between.
|
||||
whereBetween: function(qb, where) {
|
||||
return this.wrap(where.column) + ' between ? and ?';
|
||||
},
|
||||
|
||||
whereNull: function(qb, where) {
|
||||
return this.wrap(where.column) + ' is null';
|
||||
},
|
||||
|
||||
whereNotNull: function(qb, where) {
|
||||
return this.wrap(where.column) + ' is not null';
|
||||
},
|
||||
|
||||
whereRaw: function(qb, where) {
|
||||
return where.sql;
|
||||
},
|
||||
|
||||
// Compiles the `group by` columns.
|
||||
compileGroups: function(qb, groups) {
|
||||
return 'group by ' + this.columnize(groups);
|
||||
},
|
||||
|
||||
// Compiles the `having` statements.
|
||||
compileHavings: function(qb, havings) {
|
||||
return 'having ' + havings.map(function(having) {
|
||||
if (having.type === 'Raw') {
|
||||
return having.bool + ' ' + having.sql;
|
||||
}
|
||||
return having.bool + ' ' + this.wrap(having.column) + ' ' + having.operator + ' ' + this.parameter(having['value']);
|
||||
}, this).replace(/and |or /, '');
|
||||
},
|
||||
|
||||
// Compiles the `order by` statements.
|
||||
compileOrders: function(qb, orders) {
|
||||
if (orders.length > 0) {
|
||||
return 'order by ' + orders.map(function(order) {
|
||||
return '' + this.wrap(order.column) + ' ' + order.direction;
|
||||
}, this).join(', ');
|
||||
}
|
||||
},
|
||||
|
||||
// Compiles the `limit` statements.
|
||||
compileLimit: function(qb, limit) {
|
||||
return 'limit ' + limit;
|
||||
},
|
||||
|
||||
// Compiles an `offset` statement on the query.
|
||||
compileOffset: function(qb, offset) {
|
||||
return 'offset ' + offset;
|
||||
},
|
||||
|
||||
// Compiles an `insert` query, allowing for multiple
|
||||
// inserts using a single query statement.
|
||||
compileInsert: function(qb) {
|
||||
var values = qb.values;
|
||||
var table = this.wrapTable(qb.table);
|
||||
var columns = _.pluck(values[0], 0);
|
||||
var paramBlocks = [];
|
||||
|
||||
// If there are any "where" clauses, we need to omit
|
||||
// any bindings that may have been associated with them.
|
||||
if (qb.wheres.length > 0) this._clearWhereBindings(qb);
|
||||
|
||||
for (var i = 0, l = values.length; i < l; ++i) {
|
||||
paramBlocks.push("(" + this.parameterize(_.pluck(values[i], 1)) + ")");
|
||||
}
|
||||
|
||||
return "insert into " + table + " (" + this.columnize(columns) + ") values " + paramBlocks.join(', ');
|
||||
},
|
||||
|
||||
// Depending on the type of `where` clause, this will appropriately
|
||||
// remove any binding caused by "where" constraints, allowing the same
|
||||
// query to be used for `insert` and `update` without issue.
|
||||
_clearWhereBindings: function(qb) {
|
||||
var wheres = qb.wheres;
|
||||
var bindingCount = 0;
|
||||
for (var i = 0, l = wheres.length; i<l; i++) {
|
||||
var where = wheres[i];
|
||||
if (_.isArray(where.value)) {
|
||||
bindingCount += where.value.length;
|
||||
} else if (where.query) {
|
||||
bindingCount += where.query.bindings.length;
|
||||
} else {
|
||||
bindingCount += 1;
|
||||
}
|
||||
}
|
||||
qb.bindings = qb.bindings.slice(bindingCount);
|
||||
},
|
||||
|
||||
// Compiles an `update` query.
|
||||
compileUpdate: function(qb) {
|
||||
var values = qb.values;
|
||||
var table = this.wrapTable(qb.table), columns = [];
|
||||
for (var i=0, l = values.length; i < l; i++) {
|
||||
var value = values[i];
|
||||
columns.push(this.wrap(value[0]) + ' = ' + this.parameter(value[1]));
|
||||
}
|
||||
return 'update ' + table + ' set ' + columns.join(', ') + ' ' + this.compileWheres(qb);
|
||||
},
|
||||
|
||||
// Compiles a `delete` query.
|
||||
compileDelete: function(qb) {
|
||||
var table = this.wrapTable(qb.table);
|
||||
var where = !_.isEmpty(qb.wheres) ? this.compileWheres(qb) : '';
|
||||
return 'delete from ' + table + ' ' + where;
|
||||
},
|
||||
|
||||
// Compiles a `truncate` query.
|
||||
compileTruncate: function(qb) {
|
||||
return 'truncate ' + this.wrapTable(qb.table);
|
||||
},
|
||||
|
||||
wrap: function(value) {
|
||||
var segments;
|
||||
if (value instanceof Raw) return value.sql;
|
||||
if (_.isNumber(value)) return value;
|
||||
if (value.toLowerCase().indexOf(' as ') !== -1) {
|
||||
segments = value.split(' ');
|
||||
return this.wrap(segments[0]) + ' as ' + this.wrap(segments[2]);
|
||||
}
|
||||
var wrapped = [];
|
||||
segments = value.split('.');
|
||||
for (var i = 0, l = segments.length; i < l; i = ++i) {
|
||||
value = segments[i];
|
||||
if (i === 0 && segments.length > 1) {
|
||||
wrapped.push(this.wrapTable(value));
|
||||
} else {
|
||||
wrapped.push(this.wrapValue(value));
|
||||
}
|
||||
}
|
||||
return wrapped.join('.');
|
||||
},
|
||||
|
||||
wrapArray: function(values) {
|
||||
return _.map(values, this.wrap, this);
|
||||
},
|
||||
|
||||
wrapTable: function(table) {
|
||||
if (table instanceof Raw) return table.sql;
|
||||
return this.wrap(table);
|
||||
},
|
||||
|
||||
columnize: function(columns) {
|
||||
if (!_.isArray(columns)) columns = [columns];
|
||||
return _.map(columns, this.wrap, this).join(', ');
|
||||
},
|
||||
|
||||
parameterize: function(values) {
|
||||
if (!_.isArray(values)) values = [values];
|
||||
return _.map(values, this.parameter, this).join(', ');
|
||||
},
|
||||
|
||||
parameter: function(value) {
|
||||
return (value instanceof Raw ? value.sql : '?');
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
})(
|
||||
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports, module); }
|
||||
);
|
||||
186
clients/base/schemagrammar.js
Normal file
186
clients/base/schemagrammar.js
Normal file
@ -0,0 +1,186 @@
|
||||
(function(define) {
|
||||
|
||||
"use strict";
|
||||
|
||||
define(function(require, exports) {
|
||||
|
||||
var _ = require('underscore');
|
||||
var BaseGrammar = require('./grammar').Grammar;
|
||||
var SchemaBuilder = require('../../lib/schemabuilder').SchemaBuilder;
|
||||
|
||||
var Helpers = require('../../lib/helpers').Helpers;
|
||||
var Raw = require('../../lib/raw').Raw;
|
||||
|
||||
exports.SchemaGrammar = {
|
||||
|
||||
// Compile a foreign key command.
|
||||
compileForeign: function(blueprint, command) {
|
||||
var sql;
|
||||
if (command.foreignTable && command.foreignColumn) {
|
||||
var table = this.wrapTable(blueprint);
|
||||
var column = this.columnize(command.columns);
|
||||
var foreignTable = this.wrapTable(command.foreignTable);
|
||||
var foreignColumn = this.columnize(command.foreignColumn);
|
||||
|
||||
sql = "alter table " + table + " add constraint " + command.index + " ";
|
||||
sql += "foreign key (" + column + ") references " + foreignTable + " (" + foreignColumn + ")";
|
||||
|
||||
// Once we have the basic foreign key creation statement constructed we can
|
||||
// build out the syntax for what should happen on an update or delete of
|
||||
// the affected columns, which will get something like "cascade", etc.
|
||||
if (command.commandOnDelete) sql += " on delete " + command.commandOnDelete;
|
||||
if (command.commandOnUpdate) sql += " on update " + command.commandOnUpdate;
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Each of the column types have their own compiler functions which are
|
||||
// responsible for turning the column definition into its SQL format
|
||||
// for the platform. Then column modifiers are compiled and added.
|
||||
getColumns: function(blueprint) {
|
||||
var columns = [];
|
||||
for (var i = 0, l = blueprint.columns.length; i < l; i++) {
|
||||
var column = blueprint.columns[i];
|
||||
var sql = this.wrap(column) + ' ' + this.getType(column, blueprint);
|
||||
columns.push(this.addModifiers(sql, blueprint, column));
|
||||
}
|
||||
return columns;
|
||||
},
|
||||
|
||||
// Add the column modifiers to the definition.
|
||||
addModifiers: function(sql, blueprint, column) {
|
||||
for (var i = 0, l = this.modifiers.length; i < l; i++) {
|
||||
var modifier = this.modifiers[i];
|
||||
var method = "modify" + modifier;
|
||||
if (_.has(this, method)) {
|
||||
sql += this[method](blueprint, column) || '';
|
||||
}
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Get the SQL for the column data type.
|
||||
getType: function(column, blueprint) {
|
||||
return this['type' + Helpers.capitalize(column.type)](column, blueprint);
|
||||
},
|
||||
|
||||
// Add a prefix to an array of values, utilized in the client libs.
|
||||
prefixArray: function(prefix, values) {
|
||||
return _.map(values, function(value) { return prefix + ' ' + value; });
|
||||
},
|
||||
|
||||
// Wrap a table in keyword identifiers.
|
||||
wrapTable: function(table) {
|
||||
if (table instanceof SchemaBuilder) table = table.table;
|
||||
return BaseGrammar.wrapTable.call(this, table);
|
||||
},
|
||||
|
||||
// Wrap a value in keyword identifiers.
|
||||
wrap: function(value) {
|
||||
if (value && value.name) value = value.name;
|
||||
return BaseGrammar.wrap.call(this, value);
|
||||
},
|
||||
|
||||
// Format a value so that it can be used in "default" clauses.
|
||||
getDefaultValue: function(value) {
|
||||
if (value instanceof Raw) return value.sql;
|
||||
if (value === true || value === false) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
return '' + value;
|
||||
},
|
||||
|
||||
// Get the primary key command if it exists on the blueprint.
|
||||
getCommandByName: function(blueprint, name) {
|
||||
var commands = this.getCommandsByName(blueprint, name);
|
||||
if (commands.length > 0) return commands[0];
|
||||
},
|
||||
|
||||
// Get all of the commands with a given name.
|
||||
getCommandsByName: function(blueprint, name) {
|
||||
return _.filter(blueprint.commands, function(value) { return value.name == name; }) || [];
|
||||
},
|
||||
|
||||
// Used to compile any database specific items.
|
||||
compileAdditional: function() {},
|
||||
|
||||
// Compile a create table command.
|
||||
compileCreateTable: function(blueprint) {
|
||||
var columns = this.getColumns(blueprint).join(', ');
|
||||
return 'create table ' + this.wrapTable(blueprint) + ' (' + columns + ')';
|
||||
},
|
||||
|
||||
// Compile a drop table command.
|
||||
compileDropTable: function(blueprint) {
|
||||
return 'drop table ' + this.wrapTable(blueprint);
|
||||
},
|
||||
|
||||
// Compile a drop table (if exists) command.
|
||||
compileDropTableIfExists: function(blueprint) {
|
||||
return 'drop table if exists ' + this.wrapTable(blueprint);
|
||||
},
|
||||
|
||||
// Compile a drop index command.
|
||||
compileDropIndex: function(blueprint, command) {
|
||||
return 'drop index ' + command.index;
|
||||
},
|
||||
|
||||
// Create the column definition for a string type.
|
||||
typeString: function(column) {
|
||||
return "varchar(" + column.length + ")";
|
||||
},
|
||||
|
||||
// Create the column definition for a text type.
|
||||
typeText: function() {
|
||||
return 'text';
|
||||
},
|
||||
|
||||
// Create the column definition for a tiny integer type.
|
||||
typeTinyInteger: function() {
|
||||
return 'tinyint';
|
||||
},
|
||||
|
||||
// Create the column definition for a time type.
|
||||
typeTime: function() {
|
||||
return 'time';
|
||||
},
|
||||
|
||||
// Create the column definition for a date type.
|
||||
typeDate: function() {
|
||||
return 'date';
|
||||
},
|
||||
|
||||
// Create the column definition for a binary type.
|
||||
typeBinary: function() {
|
||||
return 'blob';
|
||||
},
|
||||
|
||||
// Create the column definition for a json type.
|
||||
typeJson: function() {
|
||||
return 'text';
|
||||
},
|
||||
|
||||
// Create the column definition for a uuid type.
|
||||
typeUuid: function() {
|
||||
return 'char(36)';
|
||||
},
|
||||
|
||||
// Get the SQL for a nullable column modifier.
|
||||
modifyNullable: function(blueprint, column) {
|
||||
return column.isNullable ? ' null' : ' not null';
|
||||
},
|
||||
|
||||
// Get the SQL for a default column modifier.
|
||||
modifyDefault: function(blueprint, column) {
|
||||
if (column.defaultValue != void 0) {
|
||||
return " default '" + this.getDefaultValue(column.defaultValue) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
})(
|
||||
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports, module); }
|
||||
);
|
||||
@ -1,8 +1,11 @@
|
||||
var When = require('when');
|
||||
var _ = require('underscore');
|
||||
var util = require('util');
|
||||
var base = require('./base');
|
||||
var mysql = require('mysql');
|
||||
var When = require('when');
|
||||
var _ = require('underscore');
|
||||
var util = require('util');
|
||||
var base = require('./base');
|
||||
var mysql = require('mysql');
|
||||
|
||||
var Grammar = require('./base/grammar').Grammar;
|
||||
var SchemaGrammar = require('./base/schemagrammar').SchemaGrammar;
|
||||
|
||||
// Constructor for the MysqlClient
|
||||
var MysqlClient = module.exports = function(name, options) {
|
||||
@ -72,24 +75,24 @@ _.extend(MysqlClient.prototype, base.protoProps, {
|
||||
});
|
||||
|
||||
// Extends the standard sql grammar.
|
||||
MysqlClient.grammar = {
|
||||
MysqlClient.grammar = _.defaults({
|
||||
|
||||
// The keyword identifier wrapper format.
|
||||
wrapValue: function(value) {
|
||||
return (value !== '*' ? util.format('`%s`', value) : "*");
|
||||
}
|
||||
|
||||
};
|
||||
}, Grammar);
|
||||
|
||||
// Grammar for the schema builder.
|
||||
MysqlClient.schemaGrammar = _.extend({}, base.schemaGrammar, MysqlClient.grammar, {
|
||||
MysqlClient.schemaGrammar = _.defaults({
|
||||
|
||||
// The possible column modifiers.
|
||||
modifiers: ['Unsigned', 'Nullable', 'Default', 'Increment', 'After', 'Comment'],
|
||||
|
||||
// Compile a create table command.
|
||||
compileCreateTable: function(blueprint, command) {
|
||||
var sql = base.schemaGrammar.compileCreateTable.call(this, blueprint, command);
|
||||
var sql = SchemaGrammar.compileCreateTable.call(this, blueprint, command);
|
||||
var conn = blueprint.client.connectionSettings;
|
||||
|
||||
if (conn.charset) sql += ' default character set ' + conn.charset;
|
||||
@ -271,4 +274,4 @@ MysqlClient.schemaGrammar = _.extend({}, base.schemaGrammar, MysqlClient.grammar
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}, SchemaGrammar, MysqlClient.grammar);
|
||||
|
||||
@ -4,6 +4,9 @@ var util = require('util');
|
||||
var base = require('./base');
|
||||
var pg = require('pg');
|
||||
|
||||
var Grammar = require('./base/grammar').Grammar;
|
||||
var SchemaGrammar = require('./base/schemagrammar').SchemaGrammar;
|
||||
|
||||
// Constructor for the PostgresClient
|
||||
var PostgresClient = module.exports = function(name, options) {
|
||||
base.setup.call(this, PostgresClient, name, options);
|
||||
@ -94,7 +97,7 @@ _.extend(PostgresClient.prototype, base.protoProps, {
|
||||
});
|
||||
|
||||
// Extends the standard sql grammar.
|
||||
PostgresClient.grammar = {
|
||||
PostgresClient.grammar = _.defaults({
|
||||
|
||||
// The keyword identifier wrapper format.
|
||||
wrapValue: function(value) {
|
||||
@ -133,10 +136,10 @@ PostgresClient.grammar = {
|
||||
return sql;
|
||||
}
|
||||
|
||||
};
|
||||
}, Grammar);
|
||||
|
||||
// Grammar for the schema builder.
|
||||
PostgresClient.schemaGrammar = _.extend({}, base.schemaGrammar, PostgresClient.grammar, {
|
||||
PostgresClient.schemaGrammar = _.defaults({
|
||||
|
||||
// The possible column modifiers.
|
||||
modifiers: ['Increment', 'Nullable', 'Default'],
|
||||
@ -293,4 +296,4 @@ PostgresClient.schemaGrammar = _.extend({}, base.schemaGrammar, PostgresClient.g
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}, SchemaGrammar, PostgresClient.grammar);
|
||||
|
||||
@ -5,6 +5,9 @@ var util = require('util');
|
||||
var base = require('./base');
|
||||
var sqlite3 = require('sqlite3');
|
||||
|
||||
var Grammar = require('./base/grammar').Grammar;
|
||||
var SchemaGrammar = require('./base/schemagrammar').SchemaGrammar;
|
||||
|
||||
// Constructor for the Sqlite3Client
|
||||
var Sqlite3Client = module.exports = function(name, options) {
|
||||
base.setup.call(this, Sqlite3Client, name, options);
|
||||
@ -102,7 +105,7 @@ _.extend(Sqlite3Client.prototype, base.protoProps, {
|
||||
});
|
||||
|
||||
// Extends the standard sql grammar.
|
||||
Sqlite3Client.grammar = {
|
||||
Sqlite3Client.grammar = _.defaults({
|
||||
|
||||
// The keyword identifier wrapper format.
|
||||
wrapValue: function(value) {
|
||||
@ -166,10 +169,11 @@ Sqlite3Client.grammar = {
|
||||
sql.push('delete from ' + table);
|
||||
return sql;
|
||||
}
|
||||
};
|
||||
|
||||
}, Grammar);
|
||||
|
||||
// Grammar for the schema builder.
|
||||
Sqlite3Client.schemaGrammar = _.extend({}, base.schemaGrammar, Sqlite3Client.grammar, {
|
||||
Sqlite3Client.schemaGrammar = _.defaults({
|
||||
|
||||
// The possible column modifiers.
|
||||
modifiers: ['Nullable', 'Default', 'Increment'],
|
||||
@ -319,4 +323,4 @@ Sqlite3Client.schemaGrammar = _.extend({}, base.schemaGrammar, Sqlite3Client.gra
|
||||
return ' primary key autoincrement';
|
||||
}
|
||||
}
|
||||
});
|
||||
}, SchemaGrammar, Sqlite3Client.grammar);
|
||||
|
||||
936
knex.js
936
knex.js
@ -11,8 +11,10 @@
|
||||
define(function(require, exports, module) {
|
||||
|
||||
// Required dependencies.
|
||||
var _ = require('underscore');
|
||||
var when = require('when');
|
||||
var _ = require('underscore');
|
||||
var when = require('when');
|
||||
var Common = require('./lib/common').Common;
|
||||
var Helpers = require('./lib/helpers').Helpers;
|
||||
|
||||
// `Knex` is the root namespace and a chainable function: `Knex('tableName')`
|
||||
var Knex = function(table) {
|
||||
@ -25,406 +27,6 @@ define(function(require, exports, module) {
|
||||
// Keep in sync with package.json
|
||||
Knex.VERSION = '0.2.6';
|
||||
|
||||
// Methods common to both the `Grammar` and `SchemaGrammar` interfaces,
|
||||
// used to generate the sql in one form or another.
|
||||
var Common = {
|
||||
|
||||
_debug: false,
|
||||
|
||||
_promise: null,
|
||||
|
||||
debug: function() {
|
||||
this._debug = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// For those who dislike promise interfaces.
|
||||
// Multiple calls to `exec` will resolve with the same value
|
||||
// if called more than once. Any unhandled errors will be thrown
|
||||
// after the last block.
|
||||
exec: function(callback) {
|
||||
this._promise || (this._promise = this.runQuery());
|
||||
return this._promise.then(function(resp) {
|
||||
if (callback) callback(null, resp);
|
||||
}, function(err) {
|
||||
if (callback) callback(err, null);
|
||||
}).then(null, function(err) {
|
||||
setTimeout(function() { throw err; }, 0);
|
||||
});
|
||||
},
|
||||
|
||||
// The promise interface for the query builder.
|
||||
then: function(onFulfilled, onRejected) {
|
||||
this._promise || (this._promise = this.runQuery());
|
||||
return this._promise.then(onFulfilled, onRejected);
|
||||
},
|
||||
|
||||
// Returns an array of query strings filled out with the
|
||||
// correct values based on bindings, etc. Useful for debugging.
|
||||
toString: function() {
|
||||
this.type || (this.type = 'select');
|
||||
var data = this.toSql();
|
||||
var builder = this;
|
||||
if (!_.isArray(data)) data = [data];
|
||||
return _.map(data, function(str) {
|
||||
var questionCount = 0;
|
||||
return str.replace(/\?/g, function() {
|
||||
return builder.bindings[questionCount++];
|
||||
});
|
||||
}).join('; ');
|
||||
},
|
||||
|
||||
// Explicitly sets the connection.
|
||||
connection: function(connection) {
|
||||
this._connection = connection;
|
||||
return this;
|
||||
},
|
||||
|
||||
// The connection the current query is being run on, optionally
|
||||
// specified by the `connection` method.
|
||||
_connection: false,
|
||||
|
||||
// Sets the "type" of the current query, so we can potentially place
|
||||
// `select`, `update`, `del`, etc. anywhere in the query statement
|
||||
// and have it come out fine.
|
||||
_setType: function(type) {
|
||||
if (this.type) {
|
||||
throw new Error('The query type has already been set to ' + this.type);
|
||||
}
|
||||
this.type = type;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Returns all bindings excluding the `Knex.Raw` types.
|
||||
_cleanBindings: function() {
|
||||
var bindings = this.bindings;
|
||||
var cleaned = [];
|
||||
for (var i = 0, l = bindings.length; i < l; i++) {
|
||||
if (!(bindings[i] instanceof Raw)) {
|
||||
cleaned.push(bindings[i]);
|
||||
} else {
|
||||
push.apply(cleaned, bindings[i].bindings);
|
||||
}
|
||||
}
|
||||
return cleaned;
|
||||
},
|
||||
|
||||
// Runs the query on the current builder instance and returns a promise.
|
||||
runQuery: function() {
|
||||
if (this.transaction) {
|
||||
if (!this.transaction.connection) return when.reject(new Error('The transaction has already completed.'));
|
||||
this._connection = this.transaction.connection;
|
||||
}
|
||||
|
||||
// Prep the SQL associated with the this.
|
||||
this.sql = this.toSql();
|
||||
this.bindings = this._cleanBindings();
|
||||
if (!_.isArray(this.sql)) this.sql = [this.sql];
|
||||
|
||||
var chain;
|
||||
for (var i = 0, l = this.sql.length; i < l; i++) {
|
||||
if (chain) {
|
||||
chain.then(multiQuery(this, i, chain));
|
||||
} else {
|
||||
chain = multiQuery(this, i);
|
||||
}
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Grammar
|
||||
// -------
|
||||
|
||||
// The list of different components
|
||||
var components = [
|
||||
'aggregate', 'columns', 'from',
|
||||
'joins', 'wheres', 'groups', 'havings',
|
||||
'orders', 'limit', 'offset', 'unions'
|
||||
];
|
||||
|
||||
Knex.Grammar = {
|
||||
|
||||
// Compiles the `select` statement, or nested sub-selects
|
||||
// by calling each of the component compilers, trimming out
|
||||
// the empties, and returning a generated query string.
|
||||
compileSelect: function(qb) {
|
||||
var sql = {};
|
||||
if (_.isEmpty(qb.columns)) qb.columns = ['*'];
|
||||
for (var i = 0, l = components.length; i < l; i++) {
|
||||
var component = components[i];
|
||||
var result = _.result(qb, component);
|
||||
if (result != null) {
|
||||
sql[component] = this['compile' + capitalize(component)](qb, result);
|
||||
}
|
||||
}
|
||||
return _.compact(sql).join(' ');
|
||||
},
|
||||
|
||||
// Compiles an aggregate query.
|
||||
compileAggregate: function(qb) {
|
||||
var column = this.columnize(qb.aggregate.columns);
|
||||
if (qb.isDistinct && column !== '*') {
|
||||
column = 'distinct ' + column;
|
||||
}
|
||||
return 'select ' + qb.aggregate.type + '(' + column + ') as aggregate';
|
||||
},
|
||||
|
||||
// Compiles the columns in the query, specifying if an item was distinct.
|
||||
compileColumns: function(qb, columns) {
|
||||
if (qb.aggregate != null) return;
|
||||
return (qb.isDistinct ? 'select distinct ' : 'select ') + this.columnize(columns);
|
||||
},
|
||||
|
||||
// Compiles the `from` tableName portion of the query.
|
||||
compileFrom: function(qb, table) {
|
||||
return 'from ' + this.wrapTable(table);
|
||||
},
|
||||
|
||||
// Compiles all each of the `join` clauses on the query,
|
||||
// including any nested join queries.
|
||||
compileJoins: function(qb, joins) {
|
||||
var sql = [];
|
||||
for (var i = 0, l = joins.length; i < l; i++) {
|
||||
var join = joins[i];
|
||||
var clauses = [];
|
||||
for (var i2 = 0, l2 = join.clauses.length; i2 < l2; i2++) {
|
||||
var clause = join.clauses[i2];
|
||||
clauses.push(
|
||||
[clause['bool'], this.wrap(clause['first']), clause.operator, this.wrap(clause['second'])].join(' ')
|
||||
);
|
||||
}
|
||||
clauses[0] = clauses[0].replace(/and |or /, '');
|
||||
sql.push(join.type + ' join ' + this.wrapTable(join.table) + ' on ' + clauses.join(' '));
|
||||
}
|
||||
return sql.join(' ');
|
||||
},
|
||||
|
||||
// Compiles all `where` statements on the query.
|
||||
compileWheres: function(qb) {
|
||||
var sql = [];
|
||||
var wheres = qb.wheres;
|
||||
if (wheres.length === 0) return '';
|
||||
for (var i = 0, l = wheres.length; i < l; i++) {
|
||||
var where = wheres[i];
|
||||
sql.push(where.bool + ' ' + this['where' + where.type](qb, where));
|
||||
}
|
||||
return (sql.length > 0 ? 'where ' + sql.join(' ').replace(/and |or /, '') : '');
|
||||
},
|
||||
|
||||
// Compile the "union" queries attached to the main query.
|
||||
compileUnions: function(qb) {
|
||||
var sql = '';
|
||||
for (var i = 0, l = qb.unions.length; i < l; i++) {
|
||||
var union = qb.unions[i];
|
||||
sql += (union.all ? 'union all ' : 'union ') + this.compileSelect(union.query);
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Compiles a nested where clause.
|
||||
whereNested: function(qb, where) {
|
||||
return '(' + this.compileWheres(where.query).slice(6) + ')';
|
||||
},
|
||||
|
||||
// Compiles a nested where clause.
|
||||
whereSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' ' + where.operator + ' (' + (this.compileSelect(where.query)) + ')';
|
||||
},
|
||||
|
||||
// Compiles a basic where clause.
|
||||
whereBasic: function(qb, where) {
|
||||
return this.wrap(where.column) + ' ' + where.operator + ' ' + this.parameter(where.value);
|
||||
},
|
||||
|
||||
// Compiles a basic exists clause.
|
||||
whereExists: function(qb, where) {
|
||||
return 'exists (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a basic not exists clause.
|
||||
whereNotExists: function(qb, where) {
|
||||
return 'not exists (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a where in clause.
|
||||
whereIn: function(qb, where) {
|
||||
return this.wrap(where.column) + ' in (' + this.parameterize(where.value) + ')';
|
||||
},
|
||||
|
||||
// Compiles a where not in clause.
|
||||
whereNotIn: function(qb, where) {
|
||||
return this.wrap(where.column) + ' not in (' + this.parameterize(where.value) + ')';
|
||||
},
|
||||
|
||||
// Compiles a sub-where in clause.
|
||||
whereInSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' in (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Compiles a sub-where not in clause.
|
||||
whereNotInSub: function(qb, where) {
|
||||
return this.wrap(where.column) + ' not in (' + this.compileSelect(where.query) + ')';
|
||||
},
|
||||
|
||||
// Where between.
|
||||
whereBetween: function(qb, where) {
|
||||
return this.wrap(where.column) + ' between ? and ?';
|
||||
},
|
||||
|
||||
whereNull: function(qb, where) {
|
||||
return this.wrap(where.column) + ' is null';
|
||||
},
|
||||
|
||||
whereNotNull: function(qb, where) {
|
||||
return this.wrap(where.column) + ' is not null';
|
||||
},
|
||||
|
||||
whereRaw: function(qb, where) {
|
||||
return where.sql;
|
||||
},
|
||||
|
||||
// Compiles the `group by` columns.
|
||||
compileGroups: function(qb, groups) {
|
||||
return 'group by ' + this.columnize(groups);
|
||||
},
|
||||
|
||||
// Compiles the `having` statements.
|
||||
compileHavings: function(qb, havings) {
|
||||
return 'having ' + havings.map(function(having) {
|
||||
if (having.type === 'Raw') {
|
||||
return having.bool + ' ' + having.sql;
|
||||
}
|
||||
return having.bool + ' ' + this.wrap(having.column) + ' ' + having.operator + ' ' + this.parameter(having['value']);
|
||||
}, this).replace(/and |or /, '');
|
||||
},
|
||||
|
||||
// Compiles the `order by` statements.
|
||||
compileOrders: function(qb, orders) {
|
||||
if (orders.length > 0) {
|
||||
return 'order by ' + orders.map(function(order) {
|
||||
return '' + this.wrap(order.column) + ' ' + order.direction;
|
||||
}, this).join(', ');
|
||||
}
|
||||
},
|
||||
|
||||
// Compiles the `limit` statements.
|
||||
compileLimit: function(qb, limit) {
|
||||
return 'limit ' + limit;
|
||||
},
|
||||
|
||||
// Compiles an `offset` statement on the query.
|
||||
compileOffset: function(qb, offset) {
|
||||
return 'offset ' + offset;
|
||||
},
|
||||
|
||||
// Compiles an `insert` query, allowing for multiple
|
||||
// inserts using a single query statement.
|
||||
compileInsert: function(qb) {
|
||||
var values = qb.values;
|
||||
var table = this.wrapTable(qb.table);
|
||||
var columns = _.pluck(values[0], 0);
|
||||
var paramBlocks = [];
|
||||
|
||||
// If there are any "where" clauses, we need to omit
|
||||
// any bindings that may have been associated with them.
|
||||
if (qb.wheres.length > 0) this._clearWhereBindings(qb);
|
||||
|
||||
for (var i = 0, l = values.length; i < l; ++i) {
|
||||
paramBlocks.push("(" + this.parameterize(_.pluck(values[i], 1)) + ")");
|
||||
}
|
||||
|
||||
return "insert into " + table + " (" + this.columnize(columns) + ") values " + paramBlocks.join(', ');
|
||||
},
|
||||
|
||||
// Depending on the type of `where` clause, this will appropriately
|
||||
// remove any binding caused by "where" constraints, allowing the same
|
||||
// query to be used for `insert` and `update` without issue.
|
||||
_clearWhereBindings: function(qb) {
|
||||
var wheres = qb.wheres;
|
||||
var bindingCount = 0;
|
||||
for (var i = 0, l = wheres.length; i<l; i++) {
|
||||
var where = wheres[i];
|
||||
if (_.isArray(where.value)) {
|
||||
bindingCount += where.value.length;
|
||||
} else if (where.query) {
|
||||
bindingCount += where.query.bindings.length;
|
||||
} else {
|
||||
bindingCount += 1;
|
||||
}
|
||||
}
|
||||
qb.bindings = qb.bindings.slice(bindingCount);
|
||||
},
|
||||
|
||||
// Compiles an `update` query.
|
||||
compileUpdate: function(qb) {
|
||||
var values = qb.values;
|
||||
var table = this.wrapTable(qb.table), columns = [];
|
||||
for (var i=0, l = values.length; i < l; i++) {
|
||||
var value = values[i];
|
||||
columns.push(this.wrap(value[0]) + ' = ' + this.parameter(value[1]));
|
||||
}
|
||||
return 'update ' + table + ' set ' + columns.join(', ') + ' ' + this.compileWheres(qb);
|
||||
},
|
||||
|
||||
// Compiles a `delete` query.
|
||||
compileDelete: function(qb) {
|
||||
var table = this.wrapTable(qb.table);
|
||||
var where = !_.isEmpty(qb.wheres) ? this.compileWheres(qb) : '';
|
||||
return 'delete from ' + table + ' ' + where;
|
||||
},
|
||||
|
||||
// Compiles a `truncate` query.
|
||||
compileTruncate: function(qb) {
|
||||
return 'truncate ' + this.wrapTable(qb.table);
|
||||
},
|
||||
|
||||
wrap: function(value) {
|
||||
var segments;
|
||||
if (value instanceof Raw) return value.sql;
|
||||
if (_.isNumber(value)) return value;
|
||||
if (value.toLowerCase().indexOf(' as ') !== -1) {
|
||||
segments = value.split(' ');
|
||||
return this.wrap(segments[0]) + ' as ' + this.wrap(segments[2]);
|
||||
}
|
||||
var wrapped = [];
|
||||
segments = value.split('.');
|
||||
for (var i = 0, l = segments.length; i < l; i = ++i) {
|
||||
value = segments[i];
|
||||
if (i === 0 && segments.length > 1) {
|
||||
wrapped.push(this.wrapTable(value));
|
||||
} else {
|
||||
wrapped.push(this.wrapValue(value));
|
||||
}
|
||||
}
|
||||
return wrapped.join('.');
|
||||
},
|
||||
|
||||
wrapArray: function(values) {
|
||||
return _.map(values, this.wrap, this);
|
||||
},
|
||||
|
||||
wrapTable: function(table) {
|
||||
if (table instanceof Raw) return table.sql;
|
||||
return this.wrap(table);
|
||||
},
|
||||
|
||||
columnize: function(columns) {
|
||||
if (!_.isArray(columns)) columns = [columns];
|
||||
return _.map(columns, this.wrap, this).join(', ');
|
||||
},
|
||||
|
||||
parameterize: function(values) {
|
||||
if (!_.isArray(values)) values = [values];
|
||||
return _.map(values, this.parameter, this).join(', ');
|
||||
},
|
||||
|
||||
parameter: function(value) {
|
||||
return (value instanceof Raw ? value.sql : '?');
|
||||
}
|
||||
};
|
||||
|
||||
// Knex.Builder
|
||||
// -------
|
||||
var Builder = Knex.Builder = function(table) {
|
||||
@ -474,7 +76,7 @@ define(function(require, exports, module) {
|
||||
// Compiles the current query builder.
|
||||
toSql: function() {
|
||||
this.type || (this.type = 'select');
|
||||
return this.grammar['compile' + capitalize(this.type)](this);
|
||||
return this.grammar['compile' + Helpers.capitalize(this.type)](this);
|
||||
},
|
||||
|
||||
// Clones the current query builder, including any
|
||||
@ -797,7 +399,7 @@ define(function(require, exports, module) {
|
||||
|
||||
// Sets the values for an `update` query.
|
||||
update: function(values) {
|
||||
var obj = sortObject(values);
|
||||
var obj = Helpers.sortObject(values);
|
||||
var bindings = [];
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
bindings[i] = obj[i][1];
|
||||
@ -841,7 +443,7 @@ define(function(require, exports, module) {
|
||||
_prepValues: function(values) {
|
||||
if (!_.isArray(values)) values = values ? [values] : [];
|
||||
for (var i = 0, l = values.length; i<l; i++) {
|
||||
var obj = values[i] = sortObject(values[i]);
|
||||
var obj = values[i] = Helpers.sortObject(values[i]);
|
||||
for (var i2 = 0, l2 = obj.length; i2 < l2; i2++) {
|
||||
this.bindings.push(obj[i2][1]);
|
||||
}
|
||||
@ -1045,487 +647,7 @@ define(function(require, exports, module) {
|
||||
|
||||
// Knex.SchemaBuilder
|
||||
// --------
|
||||
|
||||
var SchemaBuilder = Knex.SchemaBuilder = function(table) {
|
||||
this.table = table;
|
||||
this.columns = [];
|
||||
this.commands = [];
|
||||
this.bindings = [];
|
||||
};
|
||||
|
||||
_.extend(SchemaBuilder.prototype, Common, {
|
||||
|
||||
_source: 'SchemaBuilder',
|
||||
|
||||
// A callback from the table building `Knex.schemaBuilder` calls.
|
||||
callback: function(callback) {
|
||||
if (callback) callback.call(this, this);
|
||||
return this;
|
||||
},
|
||||
|
||||
// Get the raw sql statements for the blueprint.
|
||||
toSql: function() {
|
||||
|
||||
// Add the commands that are implied by the blueprint.
|
||||
if (this.columns.length > 0 && !this.creating()) {
|
||||
this.commands.unshift({name: 'add'});
|
||||
}
|
||||
|
||||
// Add an "additional" command, for any extra dialect-specific logic.
|
||||
this.commands.push({name: 'additional'});
|
||||
|
||||
// Add indicies
|
||||
for (var i = 0, l = this.columns.length; i < l; i++) {
|
||||
var column = this.columns[i];
|
||||
var indices = ['primary', 'unique', 'index', 'foreign'];
|
||||
|
||||
continueIndex:
|
||||
for (var i2 = 0, l2 = indices.length; i2 < l2; i2++) {
|
||||
var index = indices[i2];
|
||||
var indexVar = 'is' + capitalize(index);
|
||||
|
||||
// If the index has been specified on the given column, but is simply
|
||||
// equal to "true" (boolean), no name has been specified for this
|
||||
// index, so we will simply call the index methods without one.
|
||||
if (column[indexVar] === true) {
|
||||
this[index](column, null);
|
||||
continue continueIndex;
|
||||
|
||||
// If the index has been specified on the column and it is something
|
||||
// other than boolean true, we will assume a name was provided on
|
||||
// the index specification, and pass in the name to the method.
|
||||
} else if (_.has(column, indexVar)) {
|
||||
this[index](column.name, column[indexVar], column);
|
||||
continue continueIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var statements = [];
|
||||
|
||||
// Each type of command has a corresponding compiler function on the schema
|
||||
// grammar which is used to build the necessary SQL statements to build
|
||||
// the blueprint element, so we'll just call that compilers function.
|
||||
for (i = 0, l = this.commands.length; i < l; i++) {
|
||||
var command = this.commands[i];
|
||||
var method = 'compile' + capitalize(command.name);
|
||||
if (_.has(this.grammar, method)) {
|
||||
var sql = this.grammar[method](this, command);
|
||||
if (sql) statements = statements.concat(sql);
|
||||
}
|
||||
}
|
||||
|
||||
return statements;
|
||||
},
|
||||
|
||||
// Determine if the blueprint has a create command.
|
||||
creating: function() {
|
||||
for (var i = 0, l = this.commands.length; i < l; i++) {
|
||||
if (this.commands[i].name == 'createTable') return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Sets the engine to use when creating the table in MySql
|
||||
engine: function(name) {
|
||||
if (!this.creating()) throw new Error('The `engine` modifier may only be used while creating a table.');
|
||||
this.isEngine = name;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds a comment to the current table being created.
|
||||
comment: function(comment) {
|
||||
return this._addCommand('comment', {comment: comment});
|
||||
},
|
||||
|
||||
// Indicate that the given columns should be dropped.
|
||||
dropColumn: function(columns) {
|
||||
if (!_.isArray(columns)) columns = columns ? [columns] : [];
|
||||
return this._addCommand('dropColumn', {columns: columns});
|
||||
},
|
||||
|
||||
// Indicate that the given columns should be dropped.
|
||||
dropColumns: function() {
|
||||
return this.dropColumn(arguments);
|
||||
},
|
||||
|
||||
// Indicate that the given primary key should be dropped.
|
||||
dropPrimary: function(index) {
|
||||
return this._dropIndexCommand('dropPrimary', index);
|
||||
},
|
||||
|
||||
// Indicate that the given unique key should be dropped.
|
||||
dropUnique: function(index) {
|
||||
return this._dropIndexCommand('dropUnique', index);
|
||||
},
|
||||
|
||||
// Indicate that the given index should be dropped.
|
||||
dropIndex: function(index) {
|
||||
return this._dropIndexCommand('dropIndex', index);
|
||||
},
|
||||
|
||||
// Indicate that the given foreign key should be dropped.
|
||||
dropForeign: function(index) {
|
||||
return this._dropIndexCommand('dropForeign', index);
|
||||
},
|
||||
|
||||
// Specify the primary key(s) for the table.
|
||||
primary: function(columns, name) {
|
||||
return this._indexCommand('primary', columns, name);
|
||||
},
|
||||
|
||||
// Specify a unique index for the table.
|
||||
unique: function(columns, name) {
|
||||
return this._indexCommand('unique', columns, name);
|
||||
},
|
||||
|
||||
// Specify an index for the table.
|
||||
index: function(columns, name) {
|
||||
return this._indexCommand('index', columns, name);
|
||||
},
|
||||
|
||||
// Specify a foreign key for the table, also getting any
|
||||
// relevant info from the chain during column.
|
||||
foreign: function(column, name) {
|
||||
var chained, chainable = this._indexCommand('foreign', column, name);
|
||||
if (_.isObject(column)) {
|
||||
chained = _.pick(column, 'foreignColumn', 'foreignTable', 'commandOnDelete', 'commandOnUpdate');
|
||||
}
|
||||
return _.extend(chainable, ForeignChainable, chained);
|
||||
},
|
||||
|
||||
// Create a new auto-incrementing column on the table.
|
||||
increments: function(column) {
|
||||
return this._addColumn('integer', (column || 'id'), {autoIncrement: true, length: 11});
|
||||
},
|
||||
|
||||
// Create a new string column on the table.
|
||||
string: function(column, length) {
|
||||
return this._addColumn('string', column, {length: (length || 255)});
|
||||
},
|
||||
|
||||
// Alias varchar to string
|
||||
varchar: function(column, length) {
|
||||
return this.string(column, length);
|
||||
},
|
||||
|
||||
// Create a new text column on the table.
|
||||
text: function(column, length) {
|
||||
return this._addColumn('text', column, {length: (length || false)});
|
||||
},
|
||||
|
||||
// Create a new integer column on the table.
|
||||
integer: function(column, length) {
|
||||
return this._addColumn('integer', column, {length: (length || 11)});
|
||||
},
|
||||
|
||||
// Create a new tinyinteger column on the table.
|
||||
tinyInteger: function(column) {
|
||||
return this._addColumn('tinyInteger', column);
|
||||
},
|
||||
|
||||
// Alias for tinyinteger column.
|
||||
tinyint: function(column) {
|
||||
return this.tinyInteger(column);
|
||||
},
|
||||
|
||||
// Create a new float column on the table.
|
||||
float: function(column, precision, scale) {
|
||||
return this._addColumn('float', column, {
|
||||
precision: (precision == null ? 8 : precision),
|
||||
scale: (scale == null ? 2 : scale)
|
||||
});
|
||||
},
|
||||
|
||||
// Create a new decimal column on the table.
|
||||
decimal: function(column, precision, scale) {
|
||||
return this._addColumn('decimal', column, {
|
||||
precision: (precision == null ? 8 : precision),
|
||||
scale: (scale == null ? 2 : scale)
|
||||
});
|
||||
},
|
||||
|
||||
// Alias to "bool"
|
||||
boolean: function(column) {
|
||||
return this.bool(column);
|
||||
},
|
||||
|
||||
// Create a new boolean column on the table
|
||||
bool: function(column) {
|
||||
return this._addColumn('boolean', column);
|
||||
},
|
||||
|
||||
// Create a new date column on the table.
|
||||
date: function(column) {
|
||||
return this._addColumn('date', column);
|
||||
},
|
||||
|
||||
// Create a new date-time column on the table.
|
||||
dateTime: function(column) {
|
||||
return this._addColumn('dateTime', column);
|
||||
},
|
||||
|
||||
// Create a new time column on the table.
|
||||
time: function(column) {
|
||||
return this._addColumn('time', column);
|
||||
},
|
||||
|
||||
// Create a new timestamp column on the table.
|
||||
timestamp: function(column) {
|
||||
return this._addColumn('timestamp', column);
|
||||
},
|
||||
|
||||
// Add creation and update dateTime's to the table.
|
||||
timestamps: function() {
|
||||
this.dateTime('created_at');
|
||||
this.dateTime('updated_at');
|
||||
},
|
||||
|
||||
// Alias to enum.
|
||||
"enum": function(column, allowed) {
|
||||
return this.enu(column, allowed);
|
||||
},
|
||||
|
||||
// Create a new enum column on the table.
|
||||
enu: function(column, allowed) {
|
||||
if (!_.isArray(allowed)) allowed = [allowed];
|
||||
return this._addColumn('enum', column, {allowed: allowed});
|
||||
},
|
||||
|
||||
// Create a new bit column on the table.
|
||||
bit: function(column, length) {
|
||||
return this._addColumn('bit', column, {length: (length || false)});
|
||||
},
|
||||
|
||||
// Create a new binary column on the table.
|
||||
binary: function(column) {
|
||||
return this._addColumn('binary', column);
|
||||
},
|
||||
|
||||
// Create a new json column on the table.
|
||||
json: function(column) {
|
||||
return this._addColumn('json', column);
|
||||
},
|
||||
|
||||
// Create a new uuid column on the table.
|
||||
uuid: function(column) {
|
||||
return this._addColumn('uuid', column);
|
||||
},
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// Create a new drop index command on the blueprint.
|
||||
// If the index is an array of columns, the developer means
|
||||
// to drop an index merely by specifying the columns involved.
|
||||
_dropIndexCommand: function(type, index) {
|
||||
var columns = [];
|
||||
if (_.isArray(index)) {
|
||||
columns = index;
|
||||
index = null;
|
||||
}
|
||||
return this._indexCommand(type, columns, index);
|
||||
},
|
||||
|
||||
// Add a new index command to the blueprint.
|
||||
// If no name was specified for this index, we will create one using a basic
|
||||
// convention of the table name, followed by the columns, followed by an
|
||||
// index type, such as primary or index, which makes the index unique.
|
||||
_indexCommand: function(type, columns, index) {
|
||||
index || (index = null);
|
||||
if (!_.isArray(columns)) columns = columns ? [columns] : [];
|
||||
if (index === null) {
|
||||
var table = this.table.replace(/\.|-/g, '_');
|
||||
index = (table + '_' + _.map(columns, function(col) { return col.name || col; }).join('_') + '_' + type).toLowerCase();
|
||||
}
|
||||
return this._addCommand(type, {index: index, columns: columns});
|
||||
},
|
||||
|
||||
// Add a new column to the blueprint.
|
||||
_addColumn: function(type, name, parameters) {
|
||||
if (!name) throw new Error('A `name` must be defined to add a column');
|
||||
var column = _.extend({type: type, name: name}, ChainableColumn, parameters);
|
||||
this.columns.push(column);
|
||||
return column;
|
||||
},
|
||||
|
||||
// Add a new command to the blueprint.
|
||||
_addCommand: function(name, parameters) {
|
||||
var command = _.extend({name: name}, parameters);
|
||||
this.commands.push(command);
|
||||
return command;
|
||||
}
|
||||
});
|
||||
|
||||
var ForeignChainable = {
|
||||
|
||||
// Sets the "column" that the current column references
|
||||
// as the a foreign key
|
||||
references: function(column) {
|
||||
this.isForeign = true;
|
||||
this.foreignColumn = column || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets the "table" where the foreign key column is located.
|
||||
inTable: function(table) {
|
||||
this.foreignTable = table || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// SQL command to run "onDelete"
|
||||
onDelete: function(command) {
|
||||
this.commandOnDelete = command || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// SQL command to run "onUpdate"
|
||||
onUpdate: function(command) {
|
||||
this.commandOnUpdate = command || null;
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var ChainableColumn = _.extend({
|
||||
|
||||
// Sets the default value for a column.
|
||||
// For `boolean` columns, we'll permit 'false'
|
||||
// to be used as default values.
|
||||
defaultTo: function(value) {
|
||||
if (this.type === 'boolean') {
|
||||
if (value === 'false') value = 0;
|
||||
value = (value ? 1 : 0);
|
||||
}
|
||||
this.defaultValue = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets an integer as unsigned, is a no-op
|
||||
// if the column type is not an integer.
|
||||
unsigned: function() {
|
||||
this.isUnsigned = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Allows the column to contain null values.
|
||||
nullable: function() {
|
||||
this.isNullable = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds an index on the specified column.
|
||||
index: function(name) {
|
||||
this.isIndex = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets this column as the primary key.
|
||||
primary: function(name) {
|
||||
this.isPrimary = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets this column as unique.
|
||||
unique: function(name) {
|
||||
this.isUnique = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets the column to be inserted after another,
|
||||
// used in MySql alter tables.
|
||||
after: function(name) {
|
||||
this.isAfter = name;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds a comment to this column.
|
||||
comment: function(comment) {
|
||||
this.isCommented = comment || null;
|
||||
return this;
|
||||
}
|
||||
|
||||
}, ForeignChainable);
|
||||
|
||||
|
||||
Knex.SchemaGrammar = {
|
||||
|
||||
// Compile a foreign key command.
|
||||
compileForeign: function(blueprint, command) {
|
||||
var sql;
|
||||
if (command.foreignTable && command.foreignColumn) {
|
||||
var table = this.wrapTable(blueprint);
|
||||
var column = this.columnize(command.columns);
|
||||
var foreignTable = this.wrapTable(command.foreignTable);
|
||||
var foreignColumn = this.columnize(command.foreignColumn);
|
||||
|
||||
sql = "alter table " + table + " add constraint " + command.index + " ";
|
||||
sql += "foreign key (" + column + ") references " + foreignTable + " (" + foreignColumn + ")";
|
||||
|
||||
// Once we have the basic foreign key creation statement constructed we can
|
||||
// build out the syntax for what should happen on an update or delete of
|
||||
// the affected columns, which will get something like "cascade", etc.
|
||||
if (command.commandOnDelete) sql += " on delete " + command.commandOnDelete;
|
||||
if (command.commandOnUpdate) sql += " on update " + command.commandOnUpdate;
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Each of the column types have their own compiler functions which are
|
||||
// responsible for turning the column definition into its SQL format
|
||||
// for the platform. Then column modifiers are compiled and added.
|
||||
getColumns: function(blueprint) {
|
||||
var columns = [];
|
||||
for (var i = 0, l = blueprint.columns.length; i < l; i++) {
|
||||
var column = blueprint.columns[i];
|
||||
var sql = this.wrap(column) + ' ' + this.getType(column, blueprint);
|
||||
columns.push(this.addModifiers(sql, blueprint, column));
|
||||
}
|
||||
return columns;
|
||||
},
|
||||
|
||||
// Add the column modifiers to the definition.
|
||||
addModifiers: function(sql, blueprint, column) {
|
||||
for (var i = 0, l = this.modifiers.length; i < l; i++) {
|
||||
var modifier = this.modifiers[i];
|
||||
var method = "modify" + modifier;
|
||||
if (_.has(this, method)) {
|
||||
sql += this[method](blueprint, column) || '';
|
||||
}
|
||||
}
|
||||
return sql;
|
||||
},
|
||||
|
||||
// Get the SQL for the column data type.
|
||||
getType: function(column, blueprint) {
|
||||
return this['type' + capitalize(column.type)](column, blueprint);
|
||||
},
|
||||
|
||||
// Add a prefix to an array of values, utilized in the client libs.
|
||||
prefixArray: function(prefix, values) {
|
||||
return _.map(values, function(value) { return prefix + ' ' + value; });
|
||||
},
|
||||
|
||||
// Wrap a table in keyword identifiers.
|
||||
wrapTable: function(table) {
|
||||
if (table instanceof SchemaBuilder) table = table.table;
|
||||
return Knex.Grammar.wrapTable.call(this, table);
|
||||
},
|
||||
|
||||
// Wrap a value in keyword identifiers.
|
||||
wrap: function(value) {
|
||||
if (value && value.name) value = value.name;
|
||||
return Knex.Grammar.wrap.call(this, value);
|
||||
},
|
||||
|
||||
// Format a value so that it can be used in "default" clauses.
|
||||
getDefaultValue: function(value) {
|
||||
if (value instanceof Raw) return value.sql;
|
||||
if (value === true || value === false) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
return '' + value;
|
||||
}
|
||||
};
|
||||
Knex.SchemaBuilder = require('./lib/schemabuilder').SchemaBuilder;
|
||||
|
||||
// Knex.Raw
|
||||
// -------
|
||||
@ -1540,43 +662,9 @@ define(function(require, exports, module) {
|
||||
return Knex.Instances['main'].Raw(sql, bindings);
|
||||
};
|
||||
|
||||
var Raw = function(sql, bindings) {
|
||||
this.bindings = (!_.isArray(bindings) ? (bindings ? [bindings] : []) : bindings);
|
||||
this.sql = sql;
|
||||
};
|
||||
var Raw = require('./lib/raw').Raw;
|
||||
|
||||
_.extend(Raw.prototype, Common, {
|
||||
|
||||
_source: 'Raw',
|
||||
|
||||
// Returns the raw sql for the query.
|
||||
toSql: function() {
|
||||
return this.sql;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Simple capitalization of a word.
|
||||
var capitalize = function(word) {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||
};
|
||||
|
||||
// Sorts an object based on the names.
|
||||
var sortObject = function(obj) {
|
||||
return _.sortBy(_.pairs(obj), function(a) {
|
||||
return a[0];
|
||||
});
|
||||
};
|
||||
|
||||
// Sets up a multi-query to be executed with serial promises.
|
||||
var multiQuery = function(builder, i, chain) {
|
||||
if (chain) {
|
||||
return function() {
|
||||
return multiQuery(builder, i);
|
||||
};
|
||||
}
|
||||
return builder.client.query(_.extend({}, builder, {sql: builder.sql[i]}));
|
||||
};
|
||||
_.extend(Raw.prototype, Common)
|
||||
|
||||
// Knex.Initialize
|
||||
// -------
|
||||
@ -1622,10 +710,6 @@ define(function(require, exports, module) {
|
||||
// Creates a new instance of the db client, passing the name and options.
|
||||
client = new ClientCtor(name, _.omit(options, 'client'));
|
||||
|
||||
// Setup the grammars specific to the client.
|
||||
client.grammar = _.extend({}, Knex.Grammar, client.grammar);
|
||||
client.schemaGrammar = _.extend({}, client.grammar, Knex.SchemaGrammar, client.schemaGrammar);
|
||||
|
||||
// If this is named "default" then we're setting this on the Knex
|
||||
Target = function(table) {
|
||||
var builder = new Knex.Builder(table);
|
||||
|
||||
115
lib/common.js
Normal file
115
lib/common.js
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
var _ = require('underscore');
|
||||
var when = require('when');
|
||||
|
||||
var Raw = require('./raw').Raw;
|
||||
var Helpers = require('./helpers').Helpers;
|
||||
|
||||
// Methods common to both the `Grammar` and `SchemaGrammar` interfaces,
|
||||
// used to generate the sql in one form or another.
|
||||
exports.Common = {
|
||||
|
||||
_debug: false,
|
||||
|
||||
_promise: null,
|
||||
|
||||
debug: function() {
|
||||
this._debug = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// For those who dislike promise interfaces.
|
||||
// Multiple calls to `exec` will resolve with the same value
|
||||
// if called more than once. Any unhandled errors will be thrown
|
||||
// after the last block.
|
||||
exec: function(callback) {
|
||||
this._promise || (this._promise = this.runQuery());
|
||||
return this._promise.then(function(resp) {
|
||||
if (callback) callback(null, resp);
|
||||
}, function(err) {
|
||||
if (callback) callback(err, null);
|
||||
}).then(null, function(err) {
|
||||
setTimeout(function() { throw err; }, 0);
|
||||
});
|
||||
},
|
||||
|
||||
// The promise interface for the query builder.
|
||||
then: function(onFulfilled, onRejected) {
|
||||
this._promise || (this._promise = this.runQuery());
|
||||
return this._promise.then(onFulfilled, onRejected);
|
||||
},
|
||||
|
||||
// Returns an array of query strings filled out with the
|
||||
// correct values based on bindings, etc. Useful for debugging.
|
||||
toString: function() {
|
||||
this.type || (this.type = 'select');
|
||||
var data = this.toSql();
|
||||
var builder = this;
|
||||
if (!_.isArray(data)) data = [data];
|
||||
return _.map(data, function(str) {
|
||||
var questionCount = 0;
|
||||
return str.replace(/\?/g, function() {
|
||||
return builder.bindings[questionCount++];
|
||||
});
|
||||
}).join('; ');
|
||||
},
|
||||
|
||||
// Explicitly sets the connection.
|
||||
connection: function(connection) {
|
||||
this._connection = connection;
|
||||
return this;
|
||||
},
|
||||
|
||||
// The connection the current query is being run on, optionally
|
||||
// specified by the `connection` method.
|
||||
_connection: false,
|
||||
|
||||
// Sets the "type" of the current query, so we can potentially place
|
||||
// `select`, `update`, `del`, etc. anywhere in the query statement
|
||||
// and have it come out fine.
|
||||
_setType: function(type) {
|
||||
if (this.type) {
|
||||
throw new Error('The query type has already been set to ' + this.type);
|
||||
}
|
||||
this.type = type;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Returns all bindings excluding the `Knex.Raw` types.
|
||||
_cleanBindings: function() {
|
||||
var bindings = this.bindings;
|
||||
var cleaned = [];
|
||||
for (var i = 0, l = bindings.length; i < l; i++) {
|
||||
if (!(bindings[i] instanceof Raw)) {
|
||||
cleaned.push(bindings[i]);
|
||||
} else {
|
||||
push.apply(cleaned, bindings[i].bindings);
|
||||
}
|
||||
}
|
||||
return cleaned;
|
||||
},
|
||||
|
||||
// Runs the query on the current builder instance and returns a promise.
|
||||
runQuery: function() {
|
||||
if (this.transaction) {
|
||||
if (!this.transaction.connection) return when.reject(new Error('The transaction has already completed.'));
|
||||
this._connection = this.transaction.connection;
|
||||
}
|
||||
|
||||
// Prep the SQL associated with the this.
|
||||
this.sql = this.toSql();
|
||||
this.bindings = this._cleanBindings();
|
||||
if (!_.isArray(this.sql)) this.sql = [this.sql];
|
||||
|
||||
var chain;
|
||||
for (var i = 0, l = this.sql.length; i < l; i++) {
|
||||
if (chain) {
|
||||
chain.then(Helpers.multiQuery(this, i, chain));
|
||||
} else {
|
||||
chain = Helpers.multiQuery(this, i);
|
||||
}
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
};
|
||||
26
lib/helpers.js
Normal file
26
lib/helpers.js
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
var _ = require('underscore');
|
||||
|
||||
var Helpers = exports.Helpers = {
|
||||
|
||||
capitalize: function(word) {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||
},
|
||||
|
||||
// Sorts an object based on the names.
|
||||
sortObject: function(obj) {
|
||||
return _.sortBy(_.pairs(obj), function(a) {
|
||||
return a[0];
|
||||
});
|
||||
},
|
||||
|
||||
// Sets up a multi-query to be executed with serial promises.
|
||||
multiQuery: function(builder, i, chain) {
|
||||
if (chain) {
|
||||
return function() {
|
||||
return Helpers.multiQuery(builder, i);
|
||||
};
|
||||
}
|
||||
return builder.client.query(_.extend({}, builder, {sql: builder.sql[i]}));
|
||||
}
|
||||
};
|
||||
21
lib/raw.js
Normal file
21
lib/raw.js
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
var _ = require('underscore');
|
||||
var Common = require('./common').Common;
|
||||
|
||||
var Raw = function(sql, bindings) {
|
||||
this.bindings = (!_.isArray(bindings) ? (bindings ? [bindings] : []) : bindings);
|
||||
this.sql = sql;
|
||||
};
|
||||
|
||||
_.extend(Raw.prototype, {
|
||||
|
||||
_source: 'Raw',
|
||||
|
||||
// Returns the raw sql for the query.
|
||||
toSql: function() {
|
||||
return this.sql;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
exports.Raw = Raw;
|
||||
407
lib/schemabuilder.js
Normal file
407
lib/schemabuilder.js
Normal file
@ -0,0 +1,407 @@
|
||||
|
||||
var _ = require('underscore');
|
||||
var Raw = require('./raw').Raw;
|
||||
var Common = require('./common').Common;
|
||||
var Helpers = require('./helpers').Helpers;
|
||||
|
||||
var SchemaBuilder = function(table) {
|
||||
this.table = table;
|
||||
this.columns = [];
|
||||
this.commands = [];
|
||||
this.bindings = [];
|
||||
};
|
||||
|
||||
_.extend(SchemaBuilder.prototype, Common, {
|
||||
|
||||
_source: 'SchemaBuilder',
|
||||
|
||||
// A callback from the table building `Knex.schemaBuilder` calls.
|
||||
callback: function(callback) {
|
||||
if (callback) callback.call(this, this);
|
||||
return this;
|
||||
},
|
||||
|
||||
// Get the raw sql statements for the blueprint.
|
||||
toSql: function() {
|
||||
|
||||
// Add the commands that are implied by the blueprint.
|
||||
if (this.columns.length > 0 && !this.creating()) {
|
||||
this.commands.unshift({name: 'add'});
|
||||
}
|
||||
|
||||
// Add an "additional" command, for any extra dialect-specific logic.
|
||||
this.commands.push({name: 'additional'});
|
||||
|
||||
// Add indicies
|
||||
for (var i = 0, l = this.columns.length; i < l; i++) {
|
||||
var column = this.columns[i];
|
||||
var indices = ['primary', 'unique', 'index', 'foreign'];
|
||||
|
||||
continueIndex:
|
||||
for (var i2 = 0, l2 = indices.length; i2 < l2; i2++) {
|
||||
var index = indices[i2];
|
||||
var indexVar = 'is' + Helpers.capitalize(index);
|
||||
|
||||
// If the index has been specified on the given column, but is simply
|
||||
// equal to "true" (boolean), no name has been specified for this
|
||||
// index, so we will simply call the index methods without one.
|
||||
if (column[indexVar] === true) {
|
||||
this[index](column, null);
|
||||
continue continueIndex;
|
||||
|
||||
// If the index has been specified on the column and it is something
|
||||
// other than boolean true, we will assume a name was provided on
|
||||
// the index specification, and pass in the name to the method.
|
||||
} else if (_.has(column, indexVar)) {
|
||||
this[index](column.name, column[indexVar], column);
|
||||
continue continueIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var statements = [];
|
||||
|
||||
// Each type of command has a corresponding compiler function on the schema
|
||||
// grammar which is used to build the necessary SQL statements to build
|
||||
// the blueprint element, so we'll just call that compilers function.
|
||||
for (i = 0, l = this.commands.length; i < l; i++) {
|
||||
var command = this.commands[i];
|
||||
var method = 'compile' + Helpers.capitalize(command.name);
|
||||
if (_.has(this.grammar, method)) {
|
||||
var sql = this.grammar[method](this, command);
|
||||
if (sql) statements = statements.concat(sql);
|
||||
}
|
||||
}
|
||||
|
||||
return statements;
|
||||
},
|
||||
|
||||
// Determine if the blueprint has a create command.
|
||||
creating: function() {
|
||||
for (var i = 0, l = this.commands.length; i < l; i++) {
|
||||
if (this.commands[i].name == 'createTable') return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Sets the engine to use when creating the table in MySql
|
||||
engine: function(name) {
|
||||
if (!this.creating()) throw new Error('The `engine` modifier may only be used while creating a table.');
|
||||
this.isEngine = name;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds a comment to the current table being created.
|
||||
comment: function(comment) {
|
||||
return this._addCommand('comment', {comment: comment});
|
||||
},
|
||||
|
||||
// Indicate that the given columns should be dropped.
|
||||
dropColumn: function(columns) {
|
||||
if (!_.isArray(columns)) columns = columns ? [columns] : [];
|
||||
return this._addCommand('dropColumn', {columns: columns});
|
||||
},
|
||||
|
||||
// Indicate that the given columns should be dropped.
|
||||
dropColumns: function() {
|
||||
return this.dropColumn(arguments);
|
||||
},
|
||||
|
||||
// Indicate that the given primary key should be dropped.
|
||||
dropPrimary: function(index) {
|
||||
return this._dropIndexCommand('dropPrimary', index);
|
||||
},
|
||||
|
||||
// Indicate that the given unique key should be dropped.
|
||||
dropUnique: function(index) {
|
||||
return this._dropIndexCommand('dropUnique', index);
|
||||
},
|
||||
|
||||
// Indicate that the given index should be dropped.
|
||||
dropIndex: function(index) {
|
||||
return this._dropIndexCommand('dropIndex', index);
|
||||
},
|
||||
|
||||
// Indicate that the given foreign key should be dropped.
|
||||
dropForeign: function(index) {
|
||||
return this._dropIndexCommand('dropForeign', index);
|
||||
},
|
||||
|
||||
// Specify the primary key(s) for the table.
|
||||
primary: function(columns, name) {
|
||||
return this._indexCommand('primary', columns, name);
|
||||
},
|
||||
|
||||
// Specify a unique index for the table.
|
||||
unique: function(columns, name) {
|
||||
return this._indexCommand('unique', columns, name);
|
||||
},
|
||||
|
||||
// Specify an index for the table.
|
||||
index: function(columns, name) {
|
||||
return this._indexCommand('index', columns, name);
|
||||
},
|
||||
|
||||
// Specify a foreign key for the table, also getting any
|
||||
// relevant info from the chain during column.
|
||||
foreign: function(column, name) {
|
||||
var chained, chainable = this._indexCommand('foreign', column, name);
|
||||
if (_.isObject(column)) {
|
||||
chained = _.pick(column, 'foreignColumn', 'foreignTable', 'commandOnDelete', 'commandOnUpdate');
|
||||
}
|
||||
return _.extend(chainable, ForeignChainable, chained);
|
||||
},
|
||||
|
||||
// Create a new auto-incrementing column on the table.
|
||||
increments: function(column) {
|
||||
return this._addColumn('integer', (column || 'id'), {autoIncrement: true, length: 11});
|
||||
},
|
||||
|
||||
// Create a new string column on the table.
|
||||
string: function(column, length) {
|
||||
return this._addColumn('string', column, {length: (length || 255)});
|
||||
},
|
||||
|
||||
// Alias varchar to string
|
||||
varchar: function(column, length) {
|
||||
return this.string(column, length);
|
||||
},
|
||||
|
||||
// Create a new text column on the table.
|
||||
text: function(column, length) {
|
||||
return this._addColumn('text', column, {length: (length || false)});
|
||||
},
|
||||
|
||||
// Create a new integer column on the table.
|
||||
integer: function(column, length) {
|
||||
return this._addColumn('integer', column, {length: (length || 11)});
|
||||
},
|
||||
|
||||
// Create a new tinyinteger column on the table.
|
||||
tinyInteger: function(column) {
|
||||
return this._addColumn('tinyInteger', column);
|
||||
},
|
||||
|
||||
// Alias for tinyinteger column.
|
||||
tinyint: function(column) {
|
||||
return this.tinyInteger(column);
|
||||
},
|
||||
|
||||
// Create a new float column on the table.
|
||||
float: function(column, precision, scale) {
|
||||
return this._addColumn('float', column, {
|
||||
precision: (precision == null ? 8 : precision),
|
||||
scale: (scale == null ? 2 : scale)
|
||||
});
|
||||
},
|
||||
|
||||
// Create a new decimal column on the table.
|
||||
decimal: function(column, precision, scale) {
|
||||
return this._addColumn('decimal', column, {
|
||||
precision: (precision == null ? 8 : precision),
|
||||
scale: (scale == null ? 2 : scale)
|
||||
});
|
||||
},
|
||||
|
||||
// Alias to "bool"
|
||||
boolean: function(column) {
|
||||
return this.bool(column);
|
||||
},
|
||||
|
||||
// Create a new boolean column on the table
|
||||
bool: function(column) {
|
||||
return this._addColumn('boolean', column);
|
||||
},
|
||||
|
||||
// Create a new date column on the table.
|
||||
date: function(column) {
|
||||
return this._addColumn('date', column);
|
||||
},
|
||||
|
||||
// Create a new date-time column on the table.
|
||||
dateTime: function(column) {
|
||||
return this._addColumn('dateTime', column);
|
||||
},
|
||||
|
||||
// Create a new time column on the table.
|
||||
time: function(column) {
|
||||
return this._addColumn('time', column);
|
||||
},
|
||||
|
||||
// Create a new timestamp column on the table.
|
||||
timestamp: function(column) {
|
||||
return this._addColumn('timestamp', column);
|
||||
},
|
||||
|
||||
// Add creation and update dateTime's to the table.
|
||||
timestamps: function() {
|
||||
this.dateTime('created_at');
|
||||
this.dateTime('updated_at');
|
||||
},
|
||||
|
||||
// Alias to enum.
|
||||
"enum": function(column, allowed) {
|
||||
return this.enu(column, allowed);
|
||||
},
|
||||
|
||||
// Create a new enum column on the table.
|
||||
enu: function(column, allowed) {
|
||||
if (!_.isArray(allowed)) allowed = [allowed];
|
||||
return this._addColumn('enum', column, {allowed: allowed});
|
||||
},
|
||||
|
||||
// Create a new bit column on the table.
|
||||
bit: function(column, length) {
|
||||
return this._addColumn('bit', column, {length: (length || false)});
|
||||
},
|
||||
|
||||
// Create a new binary column on the table.
|
||||
binary: function(column) {
|
||||
return this._addColumn('binary', column);
|
||||
},
|
||||
|
||||
// Create a new json column on the table.
|
||||
json: function(column) {
|
||||
return this._addColumn('json', column);
|
||||
},
|
||||
|
||||
// Create a new uuid column on the table.
|
||||
uuid: function(column) {
|
||||
return this._addColumn('uuid', column);
|
||||
},
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// Create a new drop index command on the blueprint.
|
||||
// If the index is an array of columns, the developer means
|
||||
// to drop an index merely by specifying the columns involved.
|
||||
_dropIndexCommand: function(type, index) {
|
||||
var columns = [];
|
||||
if (_.isArray(index)) {
|
||||
columns = index;
|
||||
index = null;
|
||||
}
|
||||
return this._indexCommand(type, columns, index);
|
||||
},
|
||||
|
||||
// Add a new index command to the blueprint.
|
||||
// If no name was specified for this index, we will create one using a basic
|
||||
// convention of the table name, followed by the columns, followed by an
|
||||
// index type, such as primary or index, which makes the index unique.
|
||||
_indexCommand: function(type, columns, index) {
|
||||
index || (index = null);
|
||||
if (!_.isArray(columns)) columns = columns ? [columns] : [];
|
||||
if (index === null) {
|
||||
var table = this.table.replace(/\.|-/g, '_');
|
||||
index = (table + '_' + _.map(columns, function(col) { return col.name || col; }).join('_') + '_' + type).toLowerCase();
|
||||
}
|
||||
return this._addCommand(type, {index: index, columns: columns});
|
||||
},
|
||||
|
||||
// Add a new column to the blueprint.
|
||||
_addColumn: function(type, name, parameters) {
|
||||
if (!name) throw new Error('A `name` must be defined to add a column');
|
||||
var column = _.extend({type: type, name: name}, ChainableColumn, parameters);
|
||||
this.columns.push(column);
|
||||
return column;
|
||||
},
|
||||
|
||||
// Add a new command to the blueprint.
|
||||
_addCommand: function(name, parameters) {
|
||||
var command = _.extend({name: name}, parameters);
|
||||
this.commands.push(command);
|
||||
return command;
|
||||
}
|
||||
});
|
||||
|
||||
var ForeignChainable = {
|
||||
|
||||
// Sets the "column" that the current column references
|
||||
// as the a foreign key
|
||||
references: function(column) {
|
||||
this.isForeign = true;
|
||||
this.foreignColumn = column || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets the "table" where the foreign key column is located.
|
||||
inTable: function(table) {
|
||||
this.foreignTable = table || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// SQL command to run "onDelete"
|
||||
onDelete: function(command) {
|
||||
this.commandOnDelete = command || null;
|
||||
return this;
|
||||
},
|
||||
|
||||
// SQL command to run "onUpdate"
|
||||
onUpdate: function(command) {
|
||||
this.commandOnUpdate = command || null;
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var ChainableColumn = _.extend({
|
||||
|
||||
// Sets the default value for a column.
|
||||
// For `boolean` columns, we'll permit 'false'
|
||||
// to be used as default values.
|
||||
defaultTo: function(value) {
|
||||
if (this.type === 'boolean') {
|
||||
if (value === 'false') value = 0;
|
||||
value = (value ? 1 : 0);
|
||||
}
|
||||
this.defaultValue = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets an integer as unsigned, is a no-op
|
||||
// if the column type is not an integer.
|
||||
unsigned: function() {
|
||||
this.isUnsigned = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Allows the column to contain null values.
|
||||
nullable: function() {
|
||||
this.isNullable = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds an index on the specified column.
|
||||
index: function(name) {
|
||||
this.isIndex = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets this column as the primary key.
|
||||
primary: function(name) {
|
||||
this.isPrimary = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets this column as unique.
|
||||
unique: function(name) {
|
||||
this.isUnique = name || true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Sets the column to be inserted after another,
|
||||
// used in MySql alter tables.
|
||||
after: function(name) {
|
||||
this.isAfter = name;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Adds a comment to this column.
|
||||
comment: function(comment) {
|
||||
this.isCommented = comment || null;
|
||||
return this;
|
||||
}
|
||||
|
||||
}, ForeignChainable);
|
||||
|
||||
exports.SchemaBuilder = SchemaBuilder;
|
||||
Loading…
x
Reference in New Issue
Block a user