Queryize.js

A no-frills chainable interface for constructing mutable MySQL queries procedurally in Node.js

npm install queryize View on GitHub Report an Issue

NPM Version License: MIT Downloads/Week Build Status Coverage Gittip

var insert = queryize.insert;
var q = insert()
  .into('users', 'u')
  .set({name: 'John Doe', email: 'user@example.com'})
  .compile();

//q.query contains INSERT INTO users SET u.name = ?, u.email = ?
//q.data contains  ['John Doe', 'user@example.com']

Supports node-mysql and node-mysql2 connections and connection pools for executing queries directly.

var queryize = require('queryize');
var connection = require('mysql').createConnection('mysql://dev@localhost/testdb');

queryize()
  .from('employees').select()
  .where({city: ['Chicago', 'Miami', 'San Francisco']})
  .orderBy('lastname', 'firstname')
  .exec(connection, function (err, results) {
    if (err) console.error(err);
    else console.log(results);
  });

Extends the node-mysql query object for use as a promise.

var yesterday = new Date();
yesterday.setDate(d.getDate() - 1);

queryize.deleteFrom('eventlog')
  .whereInRange('dts', null, yesterday)
  .exec(connection)
  .then(function () {
  	console.log('Events Deleted');
  })
  .catch(function (err) {
	console.error(err);
  });

Creates a query object which encapsulates the state of the query to be generated
and provides the methods to manipulate that query.

Usage:

queryize([original])
  1. original query, Object Optional
    An existing query object to duplicate.
var queryize = require('queryize');
var query = queryize();
View Code
var queryize = function (original) {

  var query = {};

  //bind all mutators directly to query
  var proto = mapValues(mutators, function (fn) { return typeof fn === 'function' ? fn.bind(query) : fn; });

  //mix in the bound mutators
  extend(query, proto);

  query._constructor = queryize;
  query._isQueryizeObject = true;
  query._attributes = {
    debugEnabled: false,
    database: false,
    tableName: false,
    fromSubquery: false,
    alias: false,
    dataBindings: {},
    useBoundParameters: queryize.useBoundParameters,
    where: [],
    whereBoolean: null,
    set: [],
    setKeys: {},
    columns: ['*'],
    joins: [],
    orderBy: false,
    groupBy: false,
    distinct: false,
    limit: false,
    asName: false,
    builder: false
  };

  if (original) {
    if (original._isQueryizeObject) {
      extend(query._attributes, original.export());
    } else if (typeof original === 'object') {
      extend(query._attributes, original);
    }
  }

  return query;

};

Shortcut for creating a new select query

See query.select() for details.

Usage:

queryize.select()
View Code
queryize.select = function () {
  var q = queryize();
  q.select.apply(q, arguments);
  return q;
};

Shortcut for creating a new update query

See query.update() for details.

Usage:

queryize.update()
View Code
queryize.update = function () {
  var q = queryize();
  q.update.apply(q, arguments);
  return q;
};

Shortcut for creating a new insert query

See query.insert() for details.

Usage:

queryize.insert()
View Code
queryize.insert = function () {
  var q = queryize();
  q.insert.apply(q, arguments);
  return q;
};

Shortcut for creating a new replace query

See query.replace() for details.

Usage:

queryize.replace()
View Code
queryize.replace = function () {
  var q = queryize();
  q.replace.apply(q, arguments);
  return q;
};

Shortcut for creating a new delete query

See query.select() for details.

Usage:

queryize.delete()
View Code
queryize.delete = function () {
  var q = queryize();
  q.deleteFrom.apply(q, arguments);
  return q;
};

queryize.deleteFrom = queryize.delete;

Globally controls if new queries will use bound params by default.

Usage:

//disable bound params. This is not recommended
queryize.useBoundParameters = false;

Public access point for adding functions to query objects

Usage:

queryize.fn.someNewFunction = function () {
  //`this` is the current query, use `this._attributes` to store query parameters
};

If passed a truthy value, query.exec() will output the compiled query to the console.

Usage:

query.debug(enable)
  1. enable boolean
View Code
query.debug = function debug (enable) {
  this._attributes.debugEnabled = isDefined(enable) ? enable : true;

  return this;
};

Stores the passed value under a data binding with the key name.
This allows for explicit usage of bindings within query strings.

Usage:

query.insertBinding(key, value)
  1. key string
    The binding name to store the value under
  2. value mixed
    The data to be stored
View Code
query.insertBinding = function insertBinding (key, value) {
  if (typeof value === 'object') {
    // if we've got a date, convert it into a mysql ready datestamp
    if (value instanceof Date) {
      value = value.toISOString().slice(0, 19).replace('T', ' ');
    } else {
      throw new TypeError('Received unparsable object as field value.');
    }
  }

  if (key.substr(0,2) !== '{{') key = '{{' + key + '}}';

  this._attributes.dataBindings[key] = value;

  return this;
};

Stores the passed data into the data bindings collection and returns a
unique string representation of that data for use in the query.

Data bindings in queryize are represented by a key name surrounded by
double brackets. These values are then converted to question marks after
the query is compiled, with the values appended to the data array.

If a modifier function name is provided, the returned binding will be wrapped with that MySQL function.

Usage:

query.createBinding(value, [modifier])
  1. value mixed
    The data to be stored
  2. modifier string Optional
    A MySQL function to wrap the binding in when it is inserted into SQL.
query.createBinding('name@example.com');
// => "{{binding0d}}"

query.createBinding('2013-06-18', 'DATE');
// => "DATE({{binding4f}})"
View Code
query.createBinding = function createBinding (value, modifier) {
  if (value === true) return 'TRUE';
  if (value === false) return 'FALSE';
  if (value === null) return 'NULL';

  var key = '{{' + _uniqueId('binding') + '}}';

  this.insertBinding(key, value);

  return modifier ? [modifier, '(', key, ')'].join('') : key;
};

Marks the query as being a SELECT statement.

One or more columns or an array of columns to select from may be passed
in as arguments. See query.columns() for more details.

Usage:

query.select([columns])
  1. columns string, Array<string> Optional
    All arguments received are passed to a `query.columns()` call
var q = queryize()
  .select('name')
  .from('users')
  .where('user.id', 128)
  .compile()
View Code
query.select = function select () {
  this._attributes.builder = 'select';
  if (arguments.length) {
    this.columns(arrayFromArguments.apply(null, arguments));
  }
  return this;
};

Marks the query as being a DELETE statement

Supports passing the target table and alias as syntatic sugar. See query.from() for more details.

Usage:

query.delete([tablename], [alias])
  1. tablename string, Array<string> Optional
    Table to delete from. If an array is passed, defines the tables that will be deleted from in a multi-table delete.
  2. alias string Optional
    An alias to use for the table
var q = queryize()
  .delete('users')
  .where({'u.id':1})
  .compile();
var q = queryize()
  .from('logs')
  .whereInRange('dts', null, '2014-06-01')
  .deleteFrom()
  .compile()
View Code
exports.delete = query.deleteFrom = function deleteFrom (tablename, alias) {
  this._attributes.builder = 'delete';

  if (isArray(tablename)) {
    if (alias) {
      this.from(tablename.shift(), alias);
      tablename.unshift(alias);
    }
    this.columns(tablename);
  } else if (tablename) {
    this.from(tablename, alias);
  }

  return this;
};

Marks the query as being an INSERT statement

Usage:

query.insert([values])
  1. values mixed Optional
    All arguments received are passed to a `query.set()` call
queryize()
  .insert({name: 'joe'})
  .into('users')
  .exec(mysqlConnection, function (result) {
    var id = result.insertId;
  })
View Code
query.insert = function insert () {
  this._attributes.builder = 'insert';
  if (arguments.length) {
    this.set.apply(this, arguments);
  }
  return this;
};

Marks the query as being a REPLACE statement

Usage:

query.replace([values])
  1. values mixed Optional
    All arguments received are passed to a `query.set()` call
queryize()
  .replace({name: 'joe'})
  .into('users')
  .exec(mysqlConnection, function (result) {
    var id = result.insertId;
  })
View Code
query.replace = function replace () {
  this._attributes.builder = 'insert';
  this._attributes.replace = true;
  if (arguments.length) {
    this.set.apply(this, arguments);
  }
  return this;
};

Marks the query as being an UPDATE statement

Usage:

query.update([tablename], [alias])
  1. tablename string Optional
    Table to update
  2. alias string Optional
    An alias to use for the table
queryize()
  .update('users')
  .set('name', 'bob')
  .where('id', 234)
  .exec(mysqlConnection) //fire and forget
View Code
query.update = function update (tablename, alias) {
  this._attributes.builder = 'update';
  if (tablename) {
    this.table(tablename, alias);
  }
  return this;
};

Defines the table that the query should be performed on.
Can be called directly but is better called via the syntactically correct aliases.

Usage:

query.table(tablename, [alias])
  1. tablename string
  2. alias string Optional
    An alias to use for the table
queryize.update()
  .table('users')
  .set('name', 'bob')
  .where('id', 234)
  .exec(mysqlConnection) //fire and forget
var q = queryize.select()
  .from('users')
  .compile()
queryize.insert()
  .into('orders')
  .set(order)
  .exec(mysqlConnection) //fire and forget
query.from(subquery, [as])
  1. subquery query
    A query object to use as a subquery
  2. as string Optional
    Name to use for the subquery (overrides query.as() value)
queryize()
  .select('AVG(total_per_user)')
  .from(queryize()
    .select('SUM(total_invoice) AS total_per_user')
    .from('orders')
    .groupBy('userid')
    .as('order_totals');
View Code
query.table = exports.into = exports.from = function table (tablename, alias) {
  if (isQueryizeObject(tablename)) {
    if (isDefined(alias)) {
      tablename = tablename.clone().as(alias);
    }
    this._attributes.fromSubquery = this._mergeSubquery(tablename);
    return this;
  }

  this._attributes.tableName = tablename;
  if (isDefined(alias)) {
    this._attributes.alias = alias;
  }
  return this;
};

Defines what database that the query should be performed on.

This is only necessary if your connection has not defined a database
to use, or the query needs to act upon a database other than the one
currently in use.

Usage:

query.database(dbname, [tablename], [alias])
  1. dbname string
  2. tablename string Optional
  3. alias string Optional
    An alias to use for the table
View Code
query.database = exports.intoDatabase = exports.fromDatabase = function database (dbname, tablename, alias) {
  this._attributes.database = dbname;
  if (tablename) {
    this._attributes.tableName = tablename;
  }
  if (alias) {
    this._attributes.alias = alias;
  }
  return this;
};

Defines what columns a SELECT statement should return, or what tables
a DELETE statement should delete from.

Accepts either an array of columns, or multiple column arguments.

Column names can be in any format allowed in a MySQL statement.

Calling multiple times will replace the previous columns with the new set.

By default, all queries have * for SELECTs and nothing for DELETEs

Usage:

query.columns(columns, column2)
  1. columns string, Array<string>
  2. column2 ...string
var query = queryize.select()
  .columns('users.*', 'passwords.hash as password_hash')
  .from('users')
  .join('passwords', {'users.id':'passwords.user_id'})
query.columns(['username', 'firstname', 'lastname']);
query.columns(
  'users.id',
  queryize()
    .select('SUM(total_invoice)')
    .from('orders')
    .groupBy('userid')
    .as('total_invoiced')
);
View Code
query.columns = function columns () {
  var args = flatten(arrayFromArguments.apply(null, arguments));
  var self = this;

  this._attributes.columns = null;

  args.forEach(function (column) {
    self.addColumn(column);
  });

  return this;
};

Adds a single column to the columns list.

Usage:

query.addColumn(column, [avoidDuplicates])
  1. column string
    Column to add.
  2. avoidDuplicates boolean Optional
    If truthy, duplicate columns will be skipped. This value is ignored for non-string columns.
View Code
query.addColumn = function addColumn (column, avoidDuplicates) {
  // if the current columns list is only a '*' wildcard, reset the array.
  // The expected use case in this scenario is that a new queryize object was made and they want to define columns individually.
  if (this._attributes.columns && this._attributes.columns.length === 1 && this._attributes.columns[0] === '*') {
    this._attributes.columns = null;
  }

  if (typeof column === 'number' || column instanceof Date) {
    avoidDuplicates = false;
    column = this.createBinding(column);

  } else if (isQueryizeObject(column)) {
    avoidDuplicates = false;
    if (column._attributes.columns.length > 1 || column._attributes.columns[0] === '*') {
      throw new Error('Column level subqueries can only return a single column.');
    }
    column = this._mergeSubquery(column);

  } else if (typeof column === 'object' && isDefined(column.data)) {
    avoidDuplicates = false;
    column = this.createBinding(column.data, column.modifier);
  }

  if (typeof column !== 'string') {
    throw new TypeError('Unknown column type: ' + JSON.stringify(column));
  }

  if (Array.isArray(this._attributes.columns)) {
    if (!avoidDuplicates || this._attributes.columns.indexOf(column) === -1) {
      this._attributes.columns.push(column);
    }
  } else {
    this._attributes.columns = [column];
  }
};

Controls the boolean operator used to combine multiple WHERE clauses

By default, Queryize will combine all top level WHERE clauses with AND operators.

Usage:

query.comparisonMethod(condition)
  1. condition string
    "AND" or "OR"
View Code
query.comparisonMethod = function comparisonMethod (condition) {
  if (typeof condition === 'boolean') {
    condition = condition ? 'and' : 'or';
  }

  switch (String(condition).trim()) {
  case 'and':
  case 'AND':
  case 'yes':
    this._attributes.whereBoolean = 'AND'; break;

  case 'or':
  case 'OR':
  case 'no':
    this._attributes.whereBoolean = 'OR'; break;
  }

  return this;
};

Adds one or more WHERE clauses to the query.

Calling multiple times will append more clauses onto the stack.

Calling without any arguments will empty the stack.

If an operator is provided, it will be used for all comparisons derived from this call.

If a modifier function name is provided, the returned binding will be wrapped with that MySQL function.

Usage:

query.where()
//removes all existing where clauses
query.where()
query.where(clause)
  1. clause string
    A pre-written WHERE statement for direct insertion into the query
query.where('password IS NOT NULL')
// WHERE password IS NOT NULL
query.where(clauses)
  1. clause Array<string>
    Multiple pre-written WHERE statements for direct insertion into the query as OR conditions unless otherwise defined.
query.where(['account.balance > 0', 'account.gratis IS TRUE'])
// WHERE account.balance > 0 OR account.gratis IS TRUE
query.where(['AND', 'client.active IS TRUE', 'client.paidthru < NOW()'])
// WHERE client.active IS TRUE AND client.paidthru < NOW()
query.where(field, value, [operator], [modifier])
  1. field string, Array<string>
    The table field(s) to match against
  2. value string, Array<string>
    The value(s) to match with (if more than one, performs an OR comparison of each)
  3. operator string Optional (Default: '=')
    The operator to use when performing the comparison (e.g. =, !=, >, LIKE, IS NOT, etc)
  4. modifier string Optional
    A MySQL function to wrap the binding in when it is inserted into SQL.
query.where('age', 21, '<')
// WHERE age < ?
// Data: [21]
query.where('created', new Date('2014-01-01'), '>=', 'DATE')
// WHERE created >= DATE(?)
// Data: ['2014-01-01 00:00:00']
query.where(pairs, [operator], [modifier])
  1. pairs Object
    Collection of field/value pairs to match against
  2. operator string Optional (Default: '=')
    The operator to use when performing the comparison (e.g. =, !=, >, LIKE, IS NOT, etc)
  3. modifier string Optional
    A MySQL function to wrap the binding in when it is inserted into SQL.
query.where({studio:'Paramount', franchise: 'Star Trek' });
// WHERE studio = ? AND franchise = ?
// Data: ['Paramount', 'Star Trek']
query.where({'property.ownership': ['Own', 'Rent']})
// WHERE property.ownership IN (?, ?)
// Data: ['Own', 'Rent']
query.where({'user.gender':'M, not: true, 'profile.spouse': null})
// WHERE user.gender = ? AND profile.spouse != NULL
// Data: ['M']
query.where(query)
  1. query query
    A subquery to be used as a compound where clause.
query
  .where('type', [4,5,6])
  .where(queryize()
    .whereLike('address', '%' + search + '%')
    .whereLike('city', '%' + search + '%')
  )
// WHERE type IN (?,?,?) AND (address LIKE ? OR city LIKE ?)
// Data: [4, 5, 6, '%Foo%', '%Foo%']
View Code
query.where = function where (clause, value, operator, modifier) {

  if (!isDefined(clause)) {
    this._attributes.where = [];
    return this;
  }

  var self = this;

  if (isQueryizeObject(clause)) {
    clause = this._mergeCompoundWhere(clause);
  }

  // if a value is defined, then we're performing a field > value comparison
  // and must parse that first.
  if (value !== undefined && (typeof clause === 'string' || isArray(clause))) {
    clause = this._processWhereCondition(clause, value, operator, modifier);

  // if there was no value, check to see if we got an object based where definition
  } else if (typeof clause === 'object' && !isArray(clause)) {
    modifier = operator;
    operator = value;
    clause = this._processWhereObject(clause, operator, modifier);
  }

  // if we've got an array at this point, then we should parse it as if it were
  // a collection of possible conditions in an OR
  if (isArray(clause)) {
    clause = flatten(clause).map(function (c) {
      switch (typeof c) {
      case 'string': return c;
      case 'object': return self._processWhereObject(c, operator, modifier);
      default:
        throw new TypeError('Where clause could not be processed. Found ' + (typeof c) + ' instead.');
      }
    });

    var l = clause.length, subBoolean = ' OR ';
    if (l === 1) {
      clause = clause[0];
    } else if (l > 1) {
      // if the first value in the array is "AND", reverse our typical boolean.
      if (clause[0] === 'AND') {
        subBoolean = ' AND ';
        clause.shift();
        l--;
      }
      if (l === 1) {
        clause = clause[0];
      } else {
        clause = '(' + clause.join(subBoolean) + ')';
      }
    } else {
      clause = null;
    }
  }

  // by now the clause should be a string. if it isn't, then someone gave us an unusable clause
  if (typeof clause === 'string') {
    this._attributes.where.push(clause);
  } else {
    throw new TypeError('Where clause could not be processed. Found ' + (typeof clause) + ' instead.');
  }

  return this;
};

Handles basic (field, value) where clauses

Usage:

query._processWhereCondition(field, value, [operator], [modifier])
  1. field string, Array<string>
  2. value string, Array<string>
  3. operator string Optional
  4. modifier string Optional
View Code
query._processWhereCondition = function _processWhereCondition (field, value, operator, modifier) {
  var self = this;
  if (!operator) operator = '=';

  if (isArray(field)) {
    return field.map(function (field) {
      return self._processWhereCondition(field, value, operator, modifier);
    });
  }

  // if value is an array, then we need to compare against multiple values
  if (isArray(value)) {
    // if the operator is a plain equals, we should perform an IN() instead of multiple ORs
    if (operator === '=') {

      //process the values into bindings, and join the bindings inside an IN() clause
      return field + ' IN (' + value.map(function (v) { return self.createBinding(v, modifier); }).join(',') + ')';
    } else if (operator === '!=') {

      //process the values into bindings, and join the bindings inside an IN() clause
      return field + ' NOT IN (' + value.map(function (v) { return self.createBinding(v, modifier); }).join(',') + ')';

    } else {

      // process each value individually as a single condition and join the values in an OR
      return value.map(function (value) { return self._processWhereCondition(field, value, operator, modifier); });

    }
  }

  return [field, operator, self.createBinding(value, modifier)].join(' ');
};

Handles object based where clauses

Usage:

query._processWhereObject(clause, [operator], [modifier])
  1. clause Object
  2. operator string Optional
  3. modifier string Optional
View Code
query._processWhereObject = function _processWhereObject (clause, operator, modifier) {
  if (!operator) operator = '=';

  var not = false, self = this;
  clause = Object.keys(clause).map(function (field) {
    // if the object contains a 'not' key, all subsequent keys parsed will be negations.
    if (field === 'not' && clause[field] === true) {
      not = true;
      if (operator === '=') operator = '!=';
      return undefined;
    }

    return self._processWhereCondition(field, clause[field], operator, modifier);
  });

  clause = flatten(clause).filter(function (d) { return d;});

  if (clause.length === 1) {
    return clause[0];
  } else if (clause.length > 1) {
    return '(' + clause.join(' AND ') + ')';
  }

  return undefined;
};

Adds a where condition for a field between two values.

Usage:

query.whereBetween(field, from, to, modifier)
  1. field string
  2. from string, number
  3. to string, number
  4. modifier string
query.whereBetween('profile.income', 18000, 60000)
// Where profile.income BETWEEN ? AND ?
// Data: [18000, 60000]
View Code
query.whereBetween = function whereBetween (field, from, to, modifier) {
  this.where([field, 'BETWEEN', this.createBinding(from, modifier), 'AND', this.createBinding(to, modifier)].join(' '));

  return this;
};

Shortcut for performing a LIKE comparison on a field and value

Usage:

query.whereLike(field, value, [modifier])
  1. field string, Array<string>, Object
  2. value string, number, Array<string, number>
  3. modifier string Optional
queryize.select()
  .from('users')
  .whereLike('email', '%gmail.com')
View Code
query.whereLike = function whereLike (field, value, modifier) {
  if (typeof field === 'object') {
    this.where(field, 'LIKE', modifier);
  } else {
    this.where(field, value, 'LIKE', modifier);
  }
  return this;
};

Shortcut for performing an != comparisoon on a field and value

Usage:

query.whereNot(field, value, [modifier])
  1. field string, Array<string>, Object
  2. value string, number, Array<string, number>
  3. modifier string Optional
queryize.select()
  .from('users')
  .whereNot('type', 'Admin')
View Code
query.whereNot = function whereNot (field, value, modifier) {
  if (typeof field === 'object') {
    this.where(field, '!=', modifier);
  } else {
    this.where(field, value, '!=', modifier);
  }
  return this;
};

Shortcut for performing a NOT LIKE comparison on a field and value

Usage:

query.whereNotLike(field, value, [modifier])
  1. field string, Array<string>, Object
    [description]
  2. value string, number, Array<string, number>
    [description]
  3. modifier string Optional
    [description]
View Code
query.whereNotLike = function whereNotLike (field, value, modifier) {
  if (typeof field === 'object') {
    this.where(field, 'NOT LIKE', modifier);
  } else {
    this.where(field, value, 'NOT LIKE', modifier);
  }
  return this;
};

Creates a where condition for if a field fit within a boundry of values.

Omitting/passing null to the from or to arguments will make the range boundless on that side.

Usage:

query.whereInRange(field, [from], [to], [modifier])
  1. field string, Array<string>
  2. from string, number, Date Optional
  3. to string, number, Date Optional
  4. modifier string Optional
query.whereInRange('profile.income', 18000, 60000)
// Where profile.income BETWEEN ? AND ?
// Data: [18000, 60000]
query.whereInRange('age', 21)
// WHERE age >= ?
// Data: [21]
query.whereInRange('product.cost', null, 100)
// WHERE product.cost <= ?
// Data: [100]
View Code
query.whereInRange = function whereInRange (field, from, to, modifier) {
  if (isDefined(from) && isDefined(to)) {
    this.whereBetween(field, from, to, modifier);
  } else if (isDefined(from)) {
    this.where(field, from, '>=', modifier);
  } else if (isDefined(to)) {
    this.where(field, to, '<=', modifier);
  }
  return this;
};

Defines what columns a select statement should use to sort the results.
Accepts either an array of columns, or multiple column arguments.

Calling multiple times will replace the previous sort order with the new values.

Usage:

query.orderBy(columns)
  1. columns ...string, Array<string>
    Column names can be in any format allowed in a MySQL statement.
query.orderBy('category', 'DATE(date_posted) DESC');
View Code
query.orderBy = function orderBy () {
  var args = flatten(arrayFromArguments.apply(null, arguments));

  this._attributes.orderBy = args;

  return this;
};

Defines what columns and conditions a select statement should group the results under.

Accepts either an array of columns, or multiple column arguments.

Calling multiple times will replace the previous grouping rules with the new values.

Usage:

query.groupBy(columns)
  1. columns string..., Array<string>
    Column names can be in any format allowed in a MySQL statement.
query.groupBy('user.id', 'DATE(dts)');
View Code
query.groupBy = function groupBy () {
  var args = flatten(arrayFromArguments.apply(null, arguments));

  this._attributes.groupBy = args;

  return this;
};

Defines if a SELECT statement should return distinct results only

Usage:

query.distinct(enable)
  1. enable boolean
View Code
query.distinct = function distinct (enable) {
  this._attributes.distinct = isDefined(enable) ? enable : true;
  return this;
};

Defines the maximum results the query should return, and the starting offset of the first row within a set.

Usage:

query.limit(max, [offset])
  1. max number
    Total results to return
  2. offset number Optional
    Starting offset of first row within the results
View Code
query.limit = function limit (max, offset) {
  if (!isDefined(max)) max = 0;
  if (!isDefined(offset)) offset = 0;

  if (max) {
    if (offset) {
      this._attributes.limit = 'LIMIT ' + Number(offset) + ', ' + Number(max);
    } else {
      this._attributes.limit = 'LIMIT ' + Number(max);
    }
  } else {
    this._attributes.limit = false;
  }

  return this;
};

Defines what data to set the specified columns to during INSERT and UPDATE queries

Any data provided via query.set is ignored in Multi-Insert Mode. See query.addRow for more details.

Usage:

query.set(statement)
  1. statement string
    A fully written set condition
query.set(column, value, [modifier])
  1. column string
  2. value string, number
query.set(data, [modifier])
  1. data Object
    A plain object collection of column/value pairs
  2. modifier string
    [description]
query.set('user.lastlogin = NOW()')
// SET user.lastlogin = NOW()
query.set('address', '9 Pseudopolis Yard')
// SET address = ?
// Data: ['9 Pseudopolis Yard']
query.set({firstname: 'Susan', lastname: 'Sto Helet'})
// SET firstname = ?, lastname = ?
// DATA: ['Susan', 'Sto Helet']
query.set('lastLogin', {raw:'NOW()'});
// SET lastLogin = NOW()
View Code
query.set = function set (clause, value, modifier) {
  var self = this;

  if (!isDefined(clause)) throw new Error('You must define a field to set.');

  if (typeof clause === 'object') {
    Object.keys(clause).forEach(function (field) {
      self.set(field, clause[field], value);
    });
    return this;
  }

  // if we received a value, create a set clause
  if (isDefined(value)) {
    var column = clause;
    if (typeof value === 'object' && typeof value.raw === 'string') {
      clause = clause + ' = ' + value.raw;
    } else {
      if (!isValidPrimative(value)) {
        throw new TypeError('Unknown data type in set clause');
      }

      clause = clause + ' = ' + this.createBinding(value, modifier);
    }

    // if we've previously created a clause for this column, overwrite it.
    var key = this._attributes.setKeys[column];
    if (isDefined(key)) {
      this._attributes.set[key] = clause;
    } else {
      this._attributes.setKeys[column] = this._attributes.set.length;
      this._attributes.set.push(clause);
    }

  } else {
    this._attributes.set.push(clause);
  }

  return this;
};

Adds a row of data for a multi-row insert. Activates Multi-Insert Mode for the query.

About Multi-Insert Mode:

Single row inserts are performed using INSERT SET queries, where each column value is defined one at a time.
Example: INSERT INTO user SET firstname=?, lastname=?

This is more efficient for query generation and less prone to mistakes, but can only insert a single row at a time. When in Multi-Insert Mode, Queryize will construct an INSERT VALUES query where all columns are defined up front and preceded by multiple data sets.

Example: INSERT INTO user (firstname, lastname) VALUES (?,?), (?,?)

This format is more performant for inserting multiple uniform data sets, but is less flexible as all the columns must be known upfront. If row contents are provided as arrays, the column order must be defined before calling query.addRow. If keyed objects are provided then Queryize will use the keys to identify the column names to use.

Any rows with missing columns will receive NULL for the column's contents.

Usage:

query.addRow(data)
  1. data Array, Object
    May be an object where the keys represent all columns, or an array that matches the number of columns already defined.
queryize
  .insert()
  .into('users')
  .columns('name', 'email')
  .addRow(['John Smith', 'jsmith@example.com'])
  .addRow(['Jane Doe', 'jdoe@example.com'])
  .compile()
// INSERT INTO users (name, email) VALUES (?, ?), (?, ?)
// DATA: ['John Smith', 'jsmith@example.com', 'Jane Doe', 'jdoe@example.com']
queryize
  .insert()
  .into('users')
  .addRow({name: 'John Smith', email: 'jsmith@example.com'})
  .addRow({name: 'Jane Doe', email: 'jdoe@example.com', date_created: {raw:'NOW()'}})
  .compile()
// INSERT INTO users (name, email, date_created) VALUES (?, ?, NULL), (?, ?, NOW())
// DATA: ['John Smith', 'jsmith@example.com', 'Jane Doe', 'jdoe@example.com']
View Code
query.addRow = function addRow (data) {
  var row, columns;
  var self = this;

  // make sure the rows property exists
  if (!this._attributes.rows) {
    this._attributes.rows = [];
  }

  if (Array.isArray(data)) {
    columns = this._attributes.columns || [];

    // if the current columns list is only a '*' wildcard, then the developer has not defined any columns yet and we can't take array data.
    if (!this._attributes.columns || this._attributes.columns.length === 1 && this._attributes.columns[0] === '*') {
      throw new Error('You must define the insert columns first in order to pass rows as array.');
    }

    if (columns.length !== data.length) {
      throw new Error('Row does not contain the same number of entries (' + data.length + ') as the number of defined columns (' + columns.length + ').');
    }

    row = {};
    columns.forEach(function (column, i) {
      row[column] = data[i];
    });

    this._attributes.rows.push(row);

  } else if (typeof data === 'object') {
    columns = Object.keys(data);

    columns.forEach(function (column) {
      self.addColumn(column, true);
    });

    this._attributes.rows.push(data);
  } else {
    throw new TypeError("Row is not of the expected data type.");
  }

  return this;
};

Adds a table join to the query.

Queryize will append any missing JOIN command at the beginning of a statement

Usage:

query.join(statement)
  1. statement string
    Fully formed join statement
query.join('orders o ON o.client_id = c.id')
query.join('JOIN orders o ON o.client_id = c.id')
query.join('LEFT JOIN orders o ON o.client_id = c.id')
query.join(tablename, options)
  1. tablename string
  2. options Object
    Plain object containing options for the join
query.join('orders', {alias: 'o', on: 'o.client_id = c.id'})
query.join('orders', {alias: 'o', on: {'o.client_id':'c.id'})
// JOIN orders o ON o.client_id = c.id
query.join('orders', {on: {'o.client_id':'c.id', not:true, o.status: [4,5]})
// JOIN orders ON o.client_id = c.id AND o.status NOT IN (?,?)
// Data: [4, 5]
query.join('orders', {on: {'o.client_id':'c.id', not:true, o.condition: {data: 'A'}})
// JOIN orders ON o.client_id = c.id AND o.status != ?
// Data: ['A']
query.join('orders', {alias: 'o', type: 'LEFT', on: 'o.client_id = c.id'})
// LEFT JOIN orders o ON o.client_id = c.id
query.join(options)
  1. options Object
    Plain object containing options for the join
query.join({table: 'orders', alias: 'o', on: {'o.client_id':'c.id'})
query.join({table: 'orders', alias: 'o', type: 'LEFT' on: {'o.client_id':'c.id'})
// LEFT JOIN orders o ON o.client_id = c.id
query.join('orders', {on: {'o.client_id':'c.id', not:true, o.condition: {data: 'A'}})
// JOIN orders ON o.client_id = c.id AND o.status != ?
// Data: ['A']
query.join(subquery, options)
  1. subquery query
    A query object to use as a subquery
  2. options Object
    Plain object containing options for the join
queryize()
  .select('AVG(total_per_user)')
  .from(queryize()
    .select('SUM(total_invoice) AS total_per_user')
    .from('orders')
    .groupBy('userid')
    .as('order_totals');
View Code
query.join = function join (clause, options) {
  if (!isDefined(clause)) throw new Error('You must define a table to join against.');

  if (isQueryizeObject(clause)) {

    if (typeof options !== 'object') throw new Error('You must define join options when joining against a queryize subquery.');
      
    options = Object.create(options);
    options.table = clause;
    clause = options;

  } else if (typeof clause === 'object') {

    if (isArray(clause)) throw new TypeError('Join clauses can only be strings or object definitions');
    if (!clause.table) throw new Error('You must define a table to join against');

  } else if (typeof options === 'object') {

    options = Object.create(options);
    options.table = clause;
    clause = options;

  } else if (typeof clause === 'string' && clause.search(joinTest) < 0) {

    clause = 'JOIN ' + clause;

  }

  if (typeof clause === 'object') {

    var stack = [];

    if (clause.type) stack.push(clause.type);
    stack.push('JOIN');
    if (isQueryizeObject(clause.table)) {
      if (clause.alias) {
        clause.table = clause.table.clone().as(clause.alias);
      }
      stack.push(this._mergeSubquery(clause.table));
    } else if (typeof clause.table !== 'string') {
      throw new TypeError('The join table name is not a string.');
    } else {
      stack.push(clause.table);
      if (clause.alias) stack.push(clause.alias);
    }

    if (clause.using) {
      stack.push( 'USING (' + (isArray(clause.using) ? clause.using.join(', ') : clause.using) + ')' );

    } else if (clause.on) {
      stack.push( 'ON (' + this._processJoinOns(clause.on, clause.onBoolean || 'AND') + ')' );

    }

    clause = stack.join(' ');
  }

  this._attributes.joins.push(clause);

  return this;
};

Generates the closures for innerJoin, leftJoin and rightJoin

Usage:

_makeJoiner(type)
  1. type string
    Join type
View Code
function _makeJoiner(type) {
  return function (clause, options) {

    if (isQueryizeObject(clause)) {

      if (typeof options !== 'object') throw new Error('You must define join options when joining against a queryize subquery.');
        
      options = Object.create(options);
      options.table = clause;
      clause = options;

    } else if (typeof clause === 'object') {

      if (isArray(clause)) throw new TypeError('Join clauses can only be strings or object definitions');
      if (!clause.table) throw new Error('You must define a table to join against');

    } else if (typeof options === 'object') {

      options = Object.create(options);
      options.table = clause;
      clause = options;

    }

    if (typeof clause === 'object') {
      clause.type = type;
    } else {
      clause = clause.search(joinTest) > -1 ? clause.replace(joinTest, type+' JOIN ') : type+' JOIN ' + clause;
    }

    this.join(clause);

    return this;
  };
}

Shortcut for creating an INNER JOIN

See query.join() for argument details

Usage:

query.innerJoin()
View Code
query.innerJoin = _makeJoiner('INNER');

Shortcut for creating a LEFT JOIN

See query.join() for argument details

Usage:

query.leftJoin()
View Code
query.leftJoin = _makeJoiner('LEFT');

Shortcut for creating a RIGHT JOIN

See query.join() for argument details

Usage:

query.rightJoin()
View Code
query.rightJoin = _makeJoiner('RIGHT');

Processes join conditions into a standardized format that's uniformly parsable

Usage:

query._processJoinOns()
View Code
query._processJoinOns = function _processJoinOns(ons, onBoolean) {

  if (typeof ons === 'string') {
    return ons;
  }

  var self = this;

  if (isArray(ons)) {
    ons = ons.map(function (d) { return self._processJoinOns(d); });
  }

  if (typeof ons === 'object' && !isArray(ons)) {
    var not = false;
    ons = Object.keys(ons).map(function (field) {
      var value = ons[field];

      // if the object contains a 'not' key, all subsequent keys parsed will be negations.
      if (field === 'not' && value === true) {
        not = true;
        return undefined;
      }

      // if value is an array, perform an IN() on the values
      if (isArray(value)) {
        value = value.map(function (d) {return self.createBinding(d); });
        return [field, not ? 'NOT IN' : 'IN', '(', value.join(', '), ')'].join(' ');

      // if value is a string or a number, process as if a normal pairing of columns
      } else if (typeof value === 'string') {
        return [field, not ? '!=' : '=', value].join(' ');

      // finally, process the value as if it were an actual value for binding
      } else if (isValidPrimative(value)) {
        return [field, not ? '!=' : '=', self.createBinding(value)].join(' ');

      // if value is an object, verify if it's a data object, and if so create a binding for the value
      } else if (typeof value === 'object' && value.data !== undefined) {
        return [field, not ? '!=' : '=', self.createBinding(value.data, value.modifier)].join(' ');
      }

      // we don't know how to handle the value
      throw new Error('Encountered unexpected value while parsing a JOIN ON condition for ' + field);
    });
  }

  //remove any undefined or empty string values
  ons = ons.filter(function (d) { return d; });

  return ons.join(' ' + onBoolean + ' ');
};

Defines the name to be used to identify the results of a subquery

Usage:

query.as(name)
  1. name string
View Code
query.as = function asNamed (name) {
  this._attributes.asName = name;
  return this;
};

Compiles this query for use as a subquery in another query object.

Usage:

query._buildSubquery()
View Code
query._buildSubquery = function _buildSubquery () {
  if (!this._attributes.tableName && !this._attributes.fromSubquery) throw new Error('No table name has been defined for a sub-query.');

  // I couldn't find a single instance of a non-select subquery, so I'm just assuming it here
  var queryString = builders.select.call(this);
  var queryData = this._collectNamedParameters(queryString);

  queryString = '(' + queryString + ') as `' + (this._attributes.asName || _uniqueId('subquery')) + '`';

  return {
    query: queryString,
    data: queryData
  };
};

Compiles this query for use as a where clause in another query.

Usage:

query._buildCompoundWhere()
View Code
query._buildCompoundWhere = function _buildCompoundWhere () {
  var wheres = this._attributes.where.concat();

  // if the whereBoolean is defined and contains AND, the compound condition was explicitly defined as an AND.
  if (this._attributes.whereBoolean === 'AND') {
    // an array of wheres is normally processed as an OR condition.
    // prefixing it with 'AND' changes it to an AND condition
    wheres.unshift('AND');
  }

  return {
    where: wheres,
    data: this._collectNamedParameters(wheres.join(' '))
  };
};

Extracts an external query object as a subquery and merges the bound data values into this query

Usage:

query._mergeSubquery(query)
  1. query query
View Code
query._mergeSubquery = function _mergeSubquery (query) {
  if (!isQueryizeObject(query)) throw new TypeError('Provided subquery is not a queryize object.');

  var q = query._buildSubquery();

  extend(this._attributes.dataBindings, q.data);

  return q.query;
};

Extracts the where clauses of an external query object and merges the bound data values into this query

Usage:

query._mergeCompoundWhere(query)
  1. query query
View Code
query._mergeCompoundWhere = function _mergeCompoundWhere (query) {
  if (!isQueryizeObject(query)) throw new TypeError('Provided compound where is not a queryize object.');

  var q = query._buildCompoundWhere();

  extend(this._attributes.dataBindings, q.data);

  return q.where;
};

Causes queryize to insert escaped data directly into the query instead of using data bound placeholders.
This is not recommended and should only be used for debugging purposes.

You can also set queryize.useBoundParameters = false to disable databinding for all queries

Usage:

query.disableBoundParameters(enable)
  1. enable boolean
View Code
query.disableBoundParameters = function disableBoundParameters (bool) {
  this._attributes.useBoundParameters = isDefined(bool) ? !bool : false;
  return this;
};
This method is deprecated: Use disableBoundParameters instead.

Usage:

query.useBoundParameters(bool)
  1. bool Boolean
View Code
query.useBoundParameters = function (bool) {
  this.disableBoundParameters(isDefined(bool) ? !bool : true);
  return this;
};

Constructs the table name to use in queries, with backticks

Usage:

query._buildTableName()
View Code
query._buildTableName = function _buildTableName () {
  if (this._attributes.fromSubquery) {
    return this._attributes.fromSubquery;
  }

  var q = [];

  if (this._attributes.database) {
    q.push('`' + this._attributes.database + '`.');
  }

  q.push('`' + this._attributes.tableName + '`');

  if (this._attributes.alias) {
    q.push(' ' + this._attributes.alias);
  }

  return q.join('');
};

Parses the comparisonMethod value and returns a string suitable as a delimiter.

Usage:

query._buildWhereBoolean()
View Code
query._buildWhereBoolean = function _buildWhereBoolean () {
  return ' ' + (this._attributes.whereBoolean && this._attributes.whereBoolean.trim() || 'AND') + ' ';
};

Compiles the final MySQL query

Usage:

query.compile()
View Code
query.compile = function compile () {
  if (!this._attributes.builder || !builders[this._attributes.builder]) throw new Error('Query operation undefined, must identify if performing a select/update/insert/delete query.');
  if (!this._attributes.tableName && !this._attributes.fromSubquery) throw new Error('No table name has been defined');

  if (this._attributes.builder === 'insert' && this._attributes.rows) {
    this._attributes.builder = 'insertMultiple';
  }

  var query = builders[this._attributes.builder].call(this);

  if (typeof query === 'string') {
    return this._convertNamedParameters(query);
  } else {
    if (typeof query !== 'object' || !query.query) throw new Error('INTERNAL ERROR: Query builder returned unrecognized output.');
    return query;
  }
};

Processes the freshly compiled query string, replacing all data bindings with placeholders and constructs the data array
If useBoundParameters is turned off, it replaces the placeholders with their escaped values.

Usage:

query._convertNamedParameters(queryString)
  1. queryString string
    The query string to be processed
View Code
query._convertNamedParameters = function _convertNamedParameters (queryString) {
  var data = [], self = this;

  if (this._attributes.useBoundParameters) {
    queryString = queryString.replace(/({{\w*}})/g, function (match, name) {
      if (self._attributes.dataBindings[name] === undefined) throw new Error('The data binding ' + name + ' could not be found.');

      data.push(self._attributes.dataBindings[name]);

      return '?';
    });
  } else {
    queryString = queryString.replace(/({{\w*}})/g, function (match, name) {
      if (self._attributes.dataBindings[name] === undefined) throw new Error('The data binding ' + name + ' could not be found.');

      return _escapeValue(self._attributes.dataBindings[name]);
    });
  }

Processes the freshly compiled query string, finding all data bindings placeholders and collecting the values for those bindings.

Usage:

query._collectNamedParameters(queryString)
  1. queryString string
    The query string to be processed
View Code
query._collectNamedParameters = function _collectNamedParameters (queryString) {
  var data = {}, self = this;

  (queryString.match(/({{\w*}})/g) || []).forEach(function (name) {
    // Subqueries might reference data bound to the parent query, so it's ok if a value doesn't exist
    if (self._attributes.dataBindings[name] !== undefined) {
      data[name] = self._attributes.dataBindings[name];
    }
  });

  return data;
};

Compiles the MySQL query and runs it using the provided connection or connection pool from node-mysql or node-mysql2.
If the connection provided is a node-mysql2 connection, then the query will be executed as a prepared statement (connection.execute).

Usage:

query.exec(connection, [options], [callback])
  1. connection Object
    node-mysql(2) connection(Pool)
  2. options Object Optional
    Options object to be passed to `connection.query` with the query string and data mixed in.
  3. callback Function Optional
    Callback function to be invoked when the query completes.
var connection = require('mysql').createConnection('mysql://dev@localhost/testdb');
// query exec used with callback
queryize()
  .select()
  .from('employees')
  .orderBy('lastname', 'firstname')
  .exec(connection, function (err, employees) {
    if (err) console.error(err);
    else console.log(employees);
  });
// query exec used as promise
queryize()
  .select()
  .from('employees')
  .orderBy('lastname', 'firstname')
  .exec(connection)
    .then(function (employees) {
      console.log(employees);
    })
    .catch(function (err) {
      console.error(err);
    });
// insert query exec with insert id
queryize()
  .insert({
    name: 'John Doe',
    birthdate: new Date('1992-05-20')
  })
  .into('employees')
  .exec(connection)
    .then(function (response) {
      console.log(response.insertId);
    });
View Code
query.exec = query.run = function exec (connection, options, callback) {
  var func;
  if (typeof connection.execute === 'function') func = 'execute';
  else if (typeof connection.query === 'function') func = 'query';
  else throw new TypeError('Connection object is not a mysql or mysql2 connection or pool.');

  var q = this.compile();

  if (this._attributes.debugEnabled) console.log(q);

  // if the second argument is a callback, remap the arguments
  if (!callback && typeof options === 'function') {
    callback = options;
    options = null;

  // if the second argument is an options object, wrap it and apply our query & data to it.
  } else if (typeof options === 'object') {
    options = Object.create(options);
    options.sql = q.query;
    options.values = q.data;
  }

  var pcb = proxmis(callback);

  var emitter;
  if (options) {
    emitter = connection[func](options, pcb);
  } else {
    emitter = connection[func](q.query, q.data, pcb);
  }

  // if the connection was a pool, then query/execute don't return values
  emitter = emitter || {};
  emitter.then = pcb.then;
  emitter.catch = pcb.catch;

  return emitter;
};

Compiles the MySQL query and runs it using the provided connection from node-mysql or node-mysql2, returning an event emitter that fires for every row as they are received.
You must provide a connection, not a connection pool, as pools cannot return streams.
If the connection provided is a node-mysql2 connection, then the query will be executed as a prepared statement (connection.execute).

Usage:

query.emitted(connection, [options])
  1. connection Object
    node-mysql(2) connection(Pool)
  2. options Object Optional
    Options object to be passed to `connection.query`.
// query stream used as event emitter
queryize()
  .select()
  .from('employees')
  .orderBy('lastname', 'firstname')
  .emitted(connection)
    .on('result', function (row) {
      console.log(row);
    })
    .on('error', function (err) {
      console.error(err);
    })
    .on('end', function () {
      console.log('done');
    })
View Code
query.emitted = function (connection, options) {
  if (connection.getConnection) {
    throw new Error('query.stream cannot use connection pools, as pool.query does not return a stream.');
  }

  var func;
  if (typeof connection.execute === 'function') func = 'execute';
  else if (typeof connection.query === 'function') func = 'query';
  else throw new TypeError('Connection object is not a mysql or mysql2 connection or pool.');

  var q = this.compile();

  if (this._attributes.debugEnabled) console.log(q);

  if (typeof options === 'object') {
    options = Object.create(options);
    options.sql = q.query;
    options.values = q.data;
  }

  var emitter;
  if (options) {
    emitter = connection[func](options);
  } else {
    emitter = connection[func](q.query, q.data);
  }

  return emitter;
};

Copies the attribute data for the query object and returns the copy

Usage:

query.export()
View Code
query.export = function exportAttributes () {
  return clone(this._attributes, true);
};

Generates a duplicate of the current query

Usage:

query.clone()
var findCompleted = queryize()
  .from('orders')
  .where('status', 'completed')
  .select();

var archiveCompleted = a.clone()
  .update()
  .set('status', 'archived');

findCompleted.exec(connection, function (results) {
  // do something with the results and then...
  archiveCompleted.exec(connection);
});
View Code
query.clone = function clone () {
  return this._constructor(this);
};

creates a string that is unique to this runtime session

Usage:

_uniqueId([prefix])
  1. prefix string Optional
View Code
function _uniqueId (prefix) {
  var id = (++idCounter).toString(16);
  return prefix ? prefix + id : id;
}

Test if a value is defined and not null

Usage:

isDefined(value)
  1. value mixed
View Code
function isDefined (value) {
  return value !== undefined && value !== null;
}

Tests if a value is a valid type for storing in mysql

Usage:

isValidPrimative(value)
  1. value mixed
View Code
function isValidPrimative (value) {
  return typeof value === 'string' ||
    typeof value === 'boolean' ||
    typeof value === 'number' ||
    value === null ||
    (value instanceof Date);
}

Tests if the provided value is a queryize query object

Usage:

isQueryizeObject(value)
  1. value mixed
View Code
function isQueryizeObject (value) {
  return typeof value === 'object' && value._isQueryizeObject;
}

Flattens a nested array into a single level array

Usage:

flatten(input, [includingObjects])
  1. input Array
    The top level array to flatten
  2. includingObjects boolean Optional (Default: false)
    If an object is encountered and this argument is truthy, the object will also be flattened by its property values.
View Code
function flatten (input, includingObjects) {
  var result = [];

  function descend (level) {
    if (isArray(level)) {
      level.forEach(descend);
    } else if (typeof level === 'object' && includingObjects) {
      Object.keys(level).forEach(function (key) {
        descend(level[key]);
      });
    } else {
      result.push(level);
    }
  }

  descend(input);

  return result;
}

Escapes a value for use in a MySQL query without SQL injection

Usage:

_escapeValue(value)
  1. value *
    The value to be escaped
View Code
function _escapeValue (value) {
  if (value === undefined || value === null) {
    return 'NULL';
  }

  switch (typeof value) {
    case 'boolean': return (value) ? 'true' : 'false';
    case 'number': return value + '';
  }

  if (value instanceof Date) {
    value = value.toISOString().slice(0, 19).replace('T', ' ');
  }

  if (typeof value === 'object') {
    throw new TypeError('Cannot escape object');
  }

  value = value.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function( s) {
    switch(s) {
      case "\0": return "\\0";
      case "\n": return "\\n";
      case "\r": return "\\r";
      case "\b": return "\\b";
      case "\t": return "\\t";
      case "\x1a": return "\\Z";
      default: return "\\" + s;
    }
  });

  return "'" + value + "'";
}

Helper function to convert arguments to an array without triggering de-optimization in V8
MUST be called via .apply

Usage:

arrayFromArguments()
View Code
function arrayFromArguments () {
  var len = arguments.length;
  var args = new Array(len);
  for(var i = 0; i < len; ++i) {
    args[i] = arguments[i];
  }
  return args;
}

Change Log

1.0.0 / 2014-12-16

0.6.0 / 2014-09-12

0.5.1 / 2014-09-13

0.5.0 / 2014-09-12

0.4.0 / 2014-09-03

0.3.0 / 2014-09-02

0.2.0 / 2014-08-22

0.1.3 / 2014-02-17

0.1.2 / 2014-02-17

0.1.1 / 2014-02-03

0.1.1 / 2014-01-14