whereIn: function(qb, where) {
- return this.wrap(where.column) + ' in (' + this.parameterize(where.values) + ')';
+ return this.wrap(where.column) + ' in (' + this.parameterize(where.value) + ')';
},diff --git a/clients/mysql.js b/clients/mysql.js index 6dd45ebf..f2bbddaf 100644 --- a/clients/mysql.js +++ b/clients/mysql.js @@ -108,7 +108,7 @@ MysqlClient.grammar = { MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { // The possible column modifiers. - modifiers: ['Unsigned', 'Nullable', 'Default', 'Increment'], + modifiers: ['Unsigned', 'Nullable', 'Default', 'Increment', 'After'], // Compile the query to determine if a table exists. compileTableExists: function() { @@ -188,7 +188,7 @@ MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { // Compile a rename table command. compileRename: function(blueprint, command) { - return "rename table " + this.wrapTable(blueprint) + " to " + this.wrapTable(command.to); + return 'rename table ' + this.wrapTable(blueprint) + ' to ' + this.wrapTable(command.to); }, // Create the column definition for a string type. @@ -198,12 +198,26 @@ MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { // Create the column definition for a text type. typeText: function(column) { - return 'text'; + switch (column.length) { + case 'medium': + case 'mediumtext': + return 'mediumtext'; + case 'long': + case 'longtext': + return 'longtext'; + default: + return 'text'; + } }, // Create the column definition for a integer type. typeInteger: function(column) { - return 'int'; + return 'int(' + column.length + ')'; + }, + + // Create the column definition for a tiny integer type. + typeTinyInteger: function() { + return 'tinyint'; }, // Create the column definition for a float type. @@ -218,7 +232,7 @@ MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { // Create the column definition for a boolean type. typeBoolean: function(column) { - return 'tinyint'; + return 'tinyint(1)'; }, // Create the column definition for a enum type. @@ -246,6 +260,11 @@ MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { return 'timestamp default 0'; }, + // Create the column definition for a bit type. + typeBit: function(column) { + return column.length !== false ? 'bit(' + column.length + ')' : 'bit'; + }, + // Create the column definition for a binary type. typeBinary: function(column) { return 'blob'; @@ -276,4 +295,4 @@ MysqlClient.schemaGrammar = _.extend({}, MysqlClient.grammar, { return ' auto_increment primary key'; } } -}); \ No newline at end of file +}); diff --git a/clients/postgres.js b/clients/postgres.js index 73a58b64..6f651c66 100644 --- a/clients/postgres.js +++ b/clients/postgres.js @@ -43,16 +43,34 @@ exports.initialize = function (options) { }, options.pool)); }; +exports.beginTransaction = function(callback) { + var connection = this.getConnection(); + this.query("begin;", null, function(err) { + callback(err, connection); + }, connection); +}; + +exports.commitTransaction = function(connection, callback) { + this.query("commit;", null, callback, connection); +}; + +exports.rollbackTransaction = function(connection, callback) { + this.query("rollback;", null, callback, connection); +}; + // Execute a query on the database. // If the fourth parameter is set, this will be used as the connection // to the database. exports.query = function (querystring, params, callback, connection) { + if (debug) console.log([querystring, params]); + // If there is a connection, use it. if (connection) { return connection.query(querystring, params, callback); } + // Bind all of the ? to numbered vars. var questionCount = 0; querystring = querystring.replace(/\?/g, function () { questionCount++; @@ -192,8 +210,7 @@ exports.schemaGrammar = _.extend({}, grammar, { // Compile a rename table command. compileRename: function(blueprint, command) { - var from = this.wrapTable(blueprint); - return "alter table " + from + " rename to " + this.wrapTable(command.to); + return 'alter table ' + this.wrapTable(blueprint) + ' rename to ' + this.wrapTable(command.to); }, // Create the column definition for a string type. @@ -256,6 +273,11 @@ exports.schemaGrammar = _.extend({}, grammar, { return 'timestamp'; }, + // Create the column definition for a bit type. + typeBit: function(column) { + return column.length !== false ? 'bit(' + column.length + ')' : 'bit'; + }, + // Create the column definition for a binary type. typeBinary: function(column) { return 'bytea'; diff --git a/clients/sqlite3.js b/clients/sqlite3.js index 3c376517..6643ce15 100644 --- a/clients/sqlite3.js +++ b/clients/sqlite3.js @@ -40,8 +40,8 @@ exports.initialize = function (options) { destroy : function(client) { client.close(); }, - max : 10, - min : 2, + max : 1, + min : 1, idleTimeoutMillis: 30000, log : false }, options.pool)); @@ -49,6 +49,8 @@ exports.initialize = function (options) { exports.query = function (querystring, params, callback, connection, type) { + if (debug) console.log([querystring, params]); + // If there is a connection, use it. if (connection) { return connection.run(querystring, params, callback); @@ -69,7 +71,21 @@ exports.query = function (querystring, params, callback, connection, type) { }); }); +}; +exports.beginTransaction = function(callback) { + var connection = this.getConnection(); + this.query("begin;", null, function(err) { + callback(err, connection); + }, connection); +}; + +exports.commitTransaction = function(connection, callback) { + this.query("commit;", null, callback, connection); +}; + +exports.rollbackTransaction = function(connection, callback) { + this.query("rollback;", null, callback, connection); }; // Returns a mysql connection, with a __cid property uniquely @@ -280,6 +296,11 @@ exports.schemaGrammar = _.extend({}, grammar, { typeBoolean: function(column) { return 'tinyint'; }, + + // Create the column definition for a tinyint type. + typeTinyInteger: function() { + return 'tinyint'; + }, // Create the column definition for a enum type. typeEnum: function(column) { diff --git a/docs/knex.html b/docs/knex.html index 75671194..652de195 100644 --- a/docs/knex.html +++ b/docs/knex.html @@ -482,7 +482,7 @@ including any nested join queries.
whereIn: function(qb, where) {
- return this.wrap(where.column) + ' in (' + this.parameterize(where.values) + ')';
+ return this.wrap(where.column) + ' in (' + this.parameterize(where.value) + ')';
}, whereNotIn: function(qb, where) {
- return this.wrap(where.column) + ' not in (' + this.parameterize(where.values) + ')';
+ return this.wrap(where.column) + ' not in (' + this.parameterize(where.value) + ')';
}, compileInsert: function(qb, values) {
var table = this.wrapTable(qb.table);
- var columns = this.columnize(_.keys(values[0]));
- var parameters = this.parameterize(values[0]);
-
- var paramBlocks = [];
- for (var i = 0, l = values.length; i < l; ++i) {
- paramBlocks.push("(" + parameters + ")");
- }
-
- return "insert into " + table + " (" + columns + ") values " + paramBlocks.join(', ');
- },Compiles an insert, getting the id of the insert row.
If there are any "where" clauses, we need to omit +any bindings that may have been associated with them.
- compileInsertGetId: function(qb, values) {
- return this.compileInsert(qb, values);
+ if (qb.wheres.length > 0) this._clearWhereBindings(qb);
+
+ for (var i = 0, l = values.length; i < l; ++i) {
+ paramBlocks.push("(" + parameters + ")");
+ }
+
+ return "insert into " + table + " (" + columns + ") values " + paramBlocks.join(', ');
},
@@ -709,6 +708,37 @@ inserts using a single query statement.
+ 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.
Compiles a delete query.
Compiles a truncate query.
All operators used in the where clause generation.
var operators = ['=', '<', '>', '<=', '>=', 'like', 'not like', 'between', 'ilike'];
- Builder.prototype = {
-
- idAttr: 'id',Sets the tableName on the query.
from: function(tableName) {
- if (!tableName) return this.table;
- this.table = tableName;
- return this;
- },Set the idAttribute for the query.
Sets the tableName on the query.
idAttribute: function(id) {
- this.idAttr = id;
+ from: function(tableName) {
+ if (!tableName) return this.table;
+ this.table = tableName;
return this;
},
@@ -923,7 +933,7 @@ pieces that have been set thus far
clone: function() {
var item = new Builder(this.table);
var items = [
- 'isDistinct', 'idAttr', 'joins',
+ 'isDistinct', 'joins',
'wheres', 'orders', 'columns', 'bindings',
'grammar', 'connection', 'transaction'
];
@@ -953,18 +963,16 @@ pieces that have been set thus far
this.orders = [];
this.columns = [];
this.bindings = [];
- this.idAttr = Builder.prototype.idAttr;
this.isDistinct = false;
},
- toJSON: function () {
+ toJSON: function() {
return {
joins: this.joins,
wheres: this.wheres,
order: this.orders,
columns: this.columns,
bindings: this.bindings,
- idAttr: this.idAttr,
isDistinct: this.isDistinct
};
},
@@ -1187,7 +1195,7 @@ where key = value.
this.wheres.push({
type: (condition || 'In'),
column: column,
- values: values,
+ value: values,
bool: bool
});
push.apply(this.bindings, values);
@@ -1621,23 +1629,19 @@ where key = value.
- Performs an INSERT query, returning a promise.
+ Performs an insert query, returning a promise.
insert: function(values, returning) {
- var str;
- returning || (returning = this.idAttr);
if (!_.isArray(values)) values = values ? [values] : [];
- for (var i = 0, l = values.length; i < l; i++) {
- var record = values[i];
- this.bindings = this.bindings.concat(_.values(record));
- }
- if (returning) {
- str = this.grammar.compileInsertGetId(this, values, returning);
- } else {
- str = this.grammar.compileInsert(this, values);
+ for (var i = 0, l = values.length; i<l; i++) {
+ var obj = sortObject(values[i]);
+ for (var i2 = 0, l2 = obj.length; i2 < l2; i2++) {
+ this.bindings.push(obj[i2][1]);
+ }
}
+ var str = this.grammar.compileInsert(this, values);
return Knex.runQuery(this, {sql: str, bindings: this._cleanBindings(), type: 'insert'});
}, Knex.Transaction = function(container) {
-
- var connection = Knex.client.getConnection(); Knex.Transaction = function(container) { var deferred = Q.defer(); var deferred = Q.defer();
+
+ Knex.client.beginTransaction(function(err, connection) { var finish = function(type, data) {
- this.connection.end();
- this.transaction.connection = null;
- deferred[type](data);
- }; var finish = function(type, data) {
+ connection.end();
+ this.connection = null;
+ deferred[type](data);
+ }; container({
- commit: function(data) { finish.call(this, 'resolve', data); },
- rollback: function(data) { finish.call(this, 'reject', data); },
- connection: connection
+ container({
+ commit: function(data) {
+ var transaction = this;
+ Knex.client.commitTransaction(connection, function(err) {
+ if (err) throw new Error(err);
+ finish.call(transaction, 'resolve', data);
+ });
+ },
+ rollback: function(data) {
+ var transaction = this;
+ Knex.client.rollbackTransaction(connection, function(err) {
+ if (err) throw new Error(err);
+ finish.call(transaction, 'reject', data);
+ });
+ },
+ connection: connection
+ });
+
});
return deferred.promise;
@@ -2959,8 +2977,7 @@ the blueprint element, so we'll just call that compilers function.
increments: function(column) {
- column || (column = 'id');
- return this._addColumn('integer', column, {autoIncrement: true});
+ return this._addColumn('integer', (column || 'id'), {autoIncrement: true, length: 11});
},
@@ -2989,12 +3006,12 @@ the blueprint element, so we'll just call that compilers function.
- Create a new text column on the table.
+ Alias varchar to string
- text: function(column) {
- return this._addColumn('text', column);
+ varchar: function(column, length) {
+ return this.string(column, length);
},
@@ -3006,12 +3023,12 @@ the blueprint element, so we'll just call that compilers function.
- Create a new integer column on the table.
+ Create a new text column on the table.
- integer: function(column) {
- return this._addColumn('integer', column);
+ text: function(column, length) {
+ return this._addColumn('text', column, {length: (length || false)});
},
@@ -3023,6 +3040,57 @@ the blueprint element, so we'll just call that compilers function.
+ 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._addColumn('tinyInteger', column);
+ },
+
+
+
+
+
+
+
+
+ ¶
+
Create a new float column on the table.
@@ -3034,11 +3102,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new decimal column on the table.
@@ -3051,28 +3119,28 @@ the blueprint element, so we'll just call that compilers function.
-
+
boolean: function(column) {
- return this.bool(columns);
+ return this._addColumn('boolean', column);
},
-
+
Alias to "boolean".
@@ -3085,11 +3153,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new date column on the table.
@@ -3102,11 +3170,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new date-time column on the table.
@@ -3119,11 +3187,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new time column on the table.
@@ -3136,11 +3204,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new timestamp column on the table.
@@ -3153,11 +3221,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Add creation and update timestamps to the table.
@@ -3171,11 +3239,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new enum column on the table.
@@ -3188,11 +3256,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Alias to enum.
@@ -3205,11 +3273,28 @@ the blueprint element, so we'll just call that compilers function.
-
+
+
+ bit: function(column, length) {
+ return this._addColumn('bit', column, {length: (length || false)});
+ },
+
+
+
+
+
+
+
+
+ ¶
Create a new binary column on the table.
@@ -3222,11 +3307,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
@@ -3235,11 +3320,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Create a new drop index command on the blueprint.
@@ -3257,11 +3342,11 @@ the blueprint element, so we'll just call that compilers function.
-
+
Add a new index command to the blueprint.
If no name was specified for this index, we will create one using a basic
@@ -3275,7 +3360,7 @@ index type, such as primary or index, which makes the index unique.
if (!_.isArray(columns)) columns = columns ? [columns] : [];
if (index === null) {
var table = this.table.replace(/\.|-/g, '_');
- index = (table + '_' + _.map(columns, function (col) { return col.name; }).join('_') + '_' + type).toLowerCase();
+ index = (table + '_' + _.map(columns, function(col) { return col.name; }).join('_') + '_' + type).toLowerCase();
}
return this._addCommand(type, {index: index, columns: columns});
},
@@ -3283,11 +3368,11 @@ index type, such as primary or index, which makes the index unique.
-
+
Add a new column to the blueprint.
@@ -3304,11 +3389,11 @@ index type, such as primary or index, which makes the index unique.
-
+
Add a new command to the blueprint.
@@ -3324,11 +3409,11 @@ index type, such as primary or index, which makes the index unique.
-
+
Chainable object used in creating SchemaBuilder commands.
@@ -3343,11 +3428,11 @@ index type, such as primary or index, which makes the index unique.
-
+
Sets the default value for a column.
@@ -3361,11 +3446,11 @@ index type, such as primary or index, which makes the index unique.
-
+
Sets an integer as unsigned, is a no-op
if the column type is not an integer.
@@ -3380,11 +3465,11 @@ if the column type is not an integer.
-
+
Allows the column to contain null values.
@@ -3395,17 +3480,17 @@ if the column type is not an integer.
return this;
},
- index: function (name) {
+ index: function(name) {
this.isIndex = name || true;
return this;
},
- primary: function (name) {
+ primary: function(name) {
this.isPrimary = name || true;
return this;
},
- unique: function (name) {
+ unique: function(name) {
this.isUnique = name || true;
return this;
}
@@ -3414,16 +3499,22 @@ if the column type is not an integer.
var capitalize = function(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
+ };
+
+ var sortObject = function(obj) {
+ return _.sortBy(_.pairs(obj), function(a) {
+ return a[0];
+ });
};
-
+
Knex.Raw
@@ -3432,11 +3523,11 @@ if the column type is not an integer.
-
+
@@ -3448,11 +3539,11 @@ if the column type is not an integer.
-
+
Knex.runQuery
@@ -3461,11 +3552,11 @@ if the column type is not an integer.
-
+
Query runner, the context of this function is set to the caller,
(either Builder or SchemaBuilder). Checks and fails on an already
@@ -3475,8 +3566,9 @@ and returns a deferred promise.
Knex.runQuery = function(builder, data) {
- if (builder.transaction && ! builder.transaction.connection) {
- return Q.reject(new Error('The transaction has already completed.'));
+ if (builder.transaction) {
+ if (!builder.transaction.connection) return Q.reject(new Error('The transaction has already completed.'));
+ builder.connection = builder.transaction.connection;
}
var deferred = Q.defer();
Knex.client.query(data.sql, (data.bindings || []), function(err, resp) {
@@ -3490,11 +3582,11 @@ and returns a deferred promise.
-
+
Export the Knex module
diff --git a/index.html b/index.html
index b1c63ffc..0a2845a4 100644
--- a/index.html
+++ b/index.html
@@ -12,7 +12,7 @@
font-size: 14px;
line-height: 22px;
font-family: Helvetica Neue, Helvetica, Arial;
- background: #FEFFFC url(docs/images/background.png);
+ background: #f7f7f7 url(docs/images/background.png);
}
.interface {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
@@ -336,6 +336,7 @@
- – connection
- – createTable
+ - – createTable
- – dropTable
- – dropTableIfExists
- – table
@@ -719,6 +720,36 @@ Knex('users')
+Knex.Transaction(function(t) {
+
+ Knex('books')
+ .transacting(t)
+ .insert({name: 'Old Books'})
+ .then(function(row) {
+
+ return Q.all(_.map([
+ {title: 'Canterbury Tales'},
+ {title: 'Moby Dick'},
+ {title: 'Hamlet'}
+ ], function(info) {
+
+ info.row_id = row.id;
+
+ // Some validation could take place here.
+ return Knex('book').transacting(t).insert(info);
+
+ }));
+
+ })
+ .then(t.commit, t.rollback);
+
+}).then(function() {
+ console.log('3 new books saved.');
+}, function() {
+ console.log('Error saving the books.');
+});
+
+
Knex.Transaction(function (t) {
Knex('pieces')
diff --git a/index.js b/index.js
deleted file mode 100644
index 710aeb43..00000000
--- a/index.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('./knex');
\ No newline at end of file
diff --git a/knex.js b/knex.js
index 05b1c713..79a947da 100644
--- a/knex.js
+++ b/knex.js
@@ -138,12 +138,12 @@
// Compiles a where in clause.
whereIn: function(qb, where) {
- return this.wrap(where.column) + ' in (' + this.parameterize(where.values) + ')';
+ 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.values) + ')';
+ return this.wrap(where.column) + ' not in (' + this.parameterize(where.value) + ')';
},
// Compiles a sub-where in clause.
@@ -208,10 +208,14 @@
// inserts using a single query statement.
compileInsert: function(qb, values) {
var table = this.wrapTable(qb.table);
- var columns = this.columnize(_.keys(values[0]));
- var parameters = this.parameterize(values[0]);
-
+ var columns = this.columnize(_.keys(values[0]).sort());
+ var parameters = this.parameterize(_.values(values[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("(" + parameters + ")");
}
@@ -219,9 +223,23 @@
return "insert into " + table + " (" + columns + ") values " + paramBlocks.join(', ');
},
- // Compiles an `insert`, getting the id of the insert row.
- compileInsertGetId: function(qb, values) {
- return this.compileInsert(qb, values);
+ // 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