text: /**
   * Module dependencies
   */
  
  var _ = require('lodash');
  
  var SelectBuilder = require('./select');
  var WhereBuilder = require('./where');
  var utils = require('./lib/utils');
  
  
  /**
   * Sequel generator
   *
   * Given a Waterline Query Object build a SQL query.
   */
  
  var Sequel = module.exports = function(schema, options) {
  
    //console.log('Sequel::constructor', {options: options})
  
    // Store the schema values for the database structure
    this.schema = schema || {};
  
    // To solve a query, multiple query strings may be needed.
    this.queries = [];
  
    // Flag whether queries should be built using parameterized queries or not.
    // Default is true.
    this.parameterized = options && utils.object.hasOwnProperty(options, 'parameterized') ? options.parameterized : true;
  
    // Flag if things should be cast, useful for averages
    this.cast = options && utils.object.hasOwnProperty(options, 'casting') ? options.casting : false;
  
    // Flag whether the database is case-sensitive or not.
    // Default is true.
    // NOTE: This does not mean that your queries will be case sensitive. It just flags if the queries
    // should use lower or regex logic for querying.
    this.caseSensitive = options && utils.object.hasOwnProperty(options, 'caseSensitive') ? options.caseSensitive : true;
  
    // Set the escape character, default is "
    this.escapeCharacter = options && utils.object.hasOwnProperty(options, 'escapeCharacter') ? options.escapeCharacter : '"';
  
    // Set the character used to wrap values in criteria processing where statements, for all forms of lt, lte, gt, gte.
    this.criteriaWrapper = options && utils.object.hasOwnProperty(options, 'criteriaWrapper') ? options.criteriaWrapper : "'";
  
    // Set if the database can return values from things such as an insert
    this.canReturnValues = options && utils.object.hasOwnProperty(options, 'canReturnValues') ? options.canReturnValues : false;
  
    // Determine if insert values should be escaped or not
    this.escapeInserts = options && utils.object.hasOwnProperty(options, 'escapeInserts') ? options.escapeInserts : false;
  
    // Determine if aliased tablenames in DELETE queries need to be referenced before the FROM, e.g.
    // DELETE `tableName` FROM `tableName` as `otherTableName` WHERE `otherTableName`.`foo` = "bar"
    // MySQL and Oracle require this, but it doesn't work in Postgresql.
    this.declareDeleteAlias = options && utils.object.hasOwnProperty(options, 'declareDeleteAlias') ? options.declareDeleteAlias : true;
  
    // Waterline NEXT
    // These are flags that can be toggled today and expose future features. If any of the following are turned
    // on the adapter tests will probably not pass. If you toggle these know what you are getting into.
    var wlNext = options && utils.object.hasOwnProperty(options, 'wlNext') ? options.wlNext : {};
    this.wlNext = {
  
      // Case sensitive - false
      // In the next version of WL queries will be case sensitive by default.
      // Set this to true to experiement with that feature today.
      caseSensitive: utils.object.hasOwnProperty(wlNext, 'caseSensitive') ? wlNext.caseSensitive : false
  
    };
  
    this.schemaName = options && utils.object.hasOwnProperty(options, 'schemaName') ? options.schemaName : false;
  
    this.values = [];
  
    return this;
  };
  
  /**
   * Build a SQL Find Query using the defined schema.
   */
  
  Sequel.prototype.find = function find(currentTable, queryObject) {
  
    // Step 1:
    // Build out the Select statements
    var selectObject = this.select(currentTable, queryObject);
    this.queries = selectObject.select;
  
    var whereObject;
    var childQueries;
    var query;
    var values;
  
    /**
     * Step 2 - Build out the parent query.
     */
  
    whereObject = this.simpleWhere(currentTable, queryObject);
  
    this.queries[0] += ' ' + whereObject.query;
    this.values[0] = whereObject.values;
  
    /**
     * Step 3 - Build out the child query templates.
     */
  
    childQueries = this.complexWhere(currentTable, queryObject);
    this.queries = this.queries.concat(childQueries);
  
    return {
      query: this.queries,
      values: this.values
    };
  
  };
  
  /**
   * Build a SQL Count Query using the defined schema.
   */
  
  Sequel.prototype.count = function count(currentTable, queryObject) {
  
    // Escape table name
    var tableName = utils.escapeName(this.schema[currentTable].tableName, this.escapeCharacter, this.schemaName);
    var alias = utils.escapeName(this.schema[currentTable].tableName, this.escapeCharacter);
  
    // Step 1:
    // Build out the Count statements
    this.queries = ['SELECT COUNT(*) as count FROM '];
  
    var subQuery = 'SELECT * FROM ' + tableName;
  
    var whereObject;
    var childQueries;
    var query;
    var values;
  
    /**
     * Step 2 - Build out the WHERE part of the query.
     */
  
    whereObject = this.simpleWhere(currentTable, queryObject);
  
    // Append the sub-query to the COUNT so you end up with something that looks like:
    // SELECT count(*) as count FROM (SELECT * FROM table LIMIT 10 OFFSET 10) AS tableAlias;
    this.queries[0] += '(SELECT * FROM ' + tableName + ' ' + whereObject.query + ') AS ' + alias;
    this.values[0] = whereObject.values;
  
    /**
     * Step 3 - Build out the child query templates.
     */
  
    childQueries = this.complexWhere(currentTable, queryObject);
    this.queries = this.queries.concat(childQueries);
  
    return {
      query: this.queries,
      values: this.values
    };
  
  };
  
  
  /**
   * Build a SQL Create Query.
   */
  
  Sequel.prototype.create = function create(currentTable, data) {
  
    var options = {
      parameterized: this.parameterized,
      escapeCharacter: this.escapeCharacter,
      escapeInserts: this.escapeInserts,
      schemaName: this.schemaName
    };
  
    // Transform the Data object into arrays used in a parameterized query
    var attributes = utils.mapAttributes(data, options);
    var columnNames = attributes.keys.join(', ');
    var paramValues = attributes.params.join(', ');
  
    // Build Query
    var query = 'INSERT INTO ' + utils.escapeName(currentTable, this.escapeCharacter, this.schemaName) + ' (' + columnNames + ') values (' + paramValues + ')';
  
    if(this.canReturnValues) {
      query += ' RETURNING *';
    }
  
    return { query: query, values: attributes.values };
  };
  
  /**
   * Build a SQL Update Query.
   */
  
  Sequel.prototype.update = function update(currentTable, queryObject, data) {
  
    var options = {
      parameterized: this.parameterized,
      escapeCharacter: this.escapeCharacter,
      escapeInserts: this.escapeInserts,
      schemaName: this.schemaName
    };
  
    // Get the attribute identity (as opposed to the table name)
    var identity = currentTable;
    // Create the query with the tablename aliased as the identity (in case they are different)
    var query = 'UPDATE ' + utils.escapeName(currentTable, this.escapeCharacter, this.schemaName) + ' AS ' + utils.escapeName(identity, this.escapeCharacter) + ' ';
  
    // Transform the Data object into arrays used in a parameterized query
    var attributes = utils.mapAttributes(data, options);
  
    // Update the paramCount
    var paramCount = attributes.params.length + 1;
  
    // Build SET string
    var str = '';
    for(var i=0; i < attributes.keys.length; i++) {
      str += attributes.keys[i] + ' = ' + attributes.params[i] + ', ';
    }
  
    // Remove trailing comma
    str = str.slice(0, -2);
  
    query += 'SET ' + str + ' ';
  
    // Add data values to this._values
    var values = attributes.values;
  
    // Build Criteria clause
    var whereObject = this.simpleWhere(currentTable, queryObject, { paramCount: paramCount });
  
    query += ' ' + whereObject.query;
    values = values.concat(whereObject.values);
  
    if(this.canReturnValues) {
      query += ' RETURNING *';
    }
  
    return {
      query: query,
      values: values
    };
  };
  
  
  /**
   * Build Delete SQL query.
   */
  
  Sequel.prototype.destroy = function destroy(currentTable, queryObject) {
  
    // Get the attribute identity (as opposed to the table name)
    var identity = currentTable;
  
    var query = 'DELETE ' + (this.declareDeleteAlias ? utils.escapeName(identity, this.escapeCharacter) : '') + ' FROM ' + utils.escapeName(currentTable, this.escapeCharacter, this.schemaName) + ' AS ' + utils.escapeName(identity, this.escapeCharacter) + ' ';
  
    // Build Criteria clause
    var whereObject = this.simpleWhere(currentTable, queryObject);
  
    query += ' ' + whereObject.query;
    var values = whereObject.values;
  
    if(this.canReturnValues) {
      query += ' RETURNING *';
    }
  
    return {
      query: query,
      values: values
    };
  };
  
  
  /**
   * Build the select statements for a query.
   */
  
  Sequel.prototype.select = function select(currentTable, queryObject) {
    var options = {
      escapeCharacter: this.escapeCharacter,
      caseSensitive: this.caseSensitive,
      cast: this.cast,
      wlNext: this.wlNext,
      schemaName: this.schemaName
    };
  
    return new SelectBuilder(this.schema, currentTable, queryObject, options);
  };
  
  /**
   * Build the where statements for a query.
   */
  
  Sequel.prototype.simpleWhere = function simpleWhere(currentTable, queryObject, options) {
  
    var _options = {
      parameterized: this.parameterized,
      caseSensitive: this.caseSensitive,
      escapeCharacter: this.escapeCharacter,
      wlNext: this.wlNext,
      schemaName: this.schemaName,
      criteriaWrapper: this.criteriaWrapper
    };
  
    var where = new WhereBuilder(this.schema, currentTable, _options);
    return where.single(queryObject, options);
  };
  
  Sequel.prototype.complexWhere = function complexWhere(currentTable, queryObject, options) {
  
    var _options = {
      parameterized: this.parameterized,
      caseSensitive: this.caseSensitive,
      escapeCharacter: this.escapeCharacter,
      schemaName: this.schemaName,
      criteriaWrapper: this.criteriaWrapper
    };
  
    var where = new WhereBuilder(this.schema, currentTable, _options);
    return where.complex(queryObject, options);
  };
  