foxhound
Version:
A Database Query generation library.
906 lines (816 loc) • 26.6 kB
JavaScript
/**
* FoxHound ALASQL Dialect
*
* @license MIT
*
* For an ALASQL query override:
// An underscore template with the following values:
// <%= DataElements %> = Field1, Field2, Field3, Field4
// <%= Begin %> = 0
// <%= Cap %> = 10
// <%= Filter %> = WHERE StartDate > :MyStartDate
// <%= Sort %> = ORDER BY Field1
// The values are empty strings if they aren't set.
*
* @author Steven Velozo <steven@velozo.com>
* @class FoxHoundDialectALASQL
*/
var FoxHoundDialectALASQL = function(pFable)
{
//Request time from SQL server with microseconds resolution
const SQL_NOW = "NOW(3)";
_Fable = pFable;
/**
* Generate a table name from the scope.
*
* Because ALASQL is all in-memory, and can be run in two modes (anonymous
* working on arrays or table-based) we are going to make this a programmable
* value. Then we can share the code across both providers.
*
* @method: generateTableName
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateTableName = function(pParameters)
{
return ' '+pParameters.scope;
};
/**
* Escape columns, because ALASQL has more reserved KWs than most SQL dialects
*/
var escapeColumn = (pColumn, pParameters) =>
{
if (pColumn.indexOf('.') < 0)
{
return '`'+pColumn+'`';
}
else
{
// This could suck if the scope is not the same
var tmpTableName = pParameters.scope;
if (pColumn.indexOf(tmpTableName+'.') > -1)
{
return '`'+pColumn.replace(tmpTableName+'.', '')+'`';
}
else
{
// This doesn't work well but we'll try it.
return '`'+pColumn+'`';
}
}
};
/**
* Generate a field list from the array of dataElements
*
* Each entry in the dataElements is a simple string
*
* @method: generateFieldList
* @param: {Object} pParameters SQL Query Parameters
* @param {Boolean} pIsForCountClause (optional) If true, generate fields for use within a count clause.
* @return: {String} Returns the field list clause, or empty string if explicit fields are requested but cannot be fulfilled
* due to missing schema.
*/
var generateFieldList = function(pParameters, pIsForCountClause)
{
var tmpDataElements = pParameters.dataElements;
if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1)
{
if (!pIsForCountClause)
{
return ' *';
}
// we need to list all of the table fields explicitly; get them from the schema
const tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
if (tmpSchema.length < 1)
{
// this means we have no schema; returning an empty string here signals the calling code to handle this case
return '';
}
const idColumn = tmpSchema.find((entry) => entry.Type === 'AutoIdentity');
if (!idColumn)
{
// this means there is no autoincrementing unique ID column; treat as above
return '';
}
return ` ${idColumn.Column}`;
}
var tmpFieldList = ' ';
for (var i = 0; i < tmpDataElements.length; i++)
{
if (i > 0)
{
tmpFieldList += ', ';
}
tmpFieldList += escapeColumn(tmpDataElements[i], pParameters);
}
return tmpFieldList;
};
/**
* Generate a query from the array of where clauses
*
* Each clause is an object like:
{
Column:'Name',
Operator:'EQ',
Value:'John',
Connector:'And',
Parameter:'Name'
}
*
* @method: generateWhere
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
*/
var generateWhere = function(pParameters)
{
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
var tmpTableName = generateTableName(pParameters).trim();
if (!pParameters.query.disableDeleteTracking)
{
// Check if there is a Deleted column on the Schema. If so, we add this to the filters automatically (if not already present)
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
for (var i = 0; i < tmpSchema.length; i++)
{
// There is a schema entry for it. Process it accordingly.
var tmpSchemaEntry = tmpSchema[i];
if (tmpSchemaEntry.Type === 'Deleted')
{
var tmpHasDeletedParameter = false;
//first, check to see if filters are already looking for Deleted column
if (tmpFilter.length > 0)
{
for (var x = 0; x < tmpFilter.length; x++)
{
if (tmpFilter[x].Column === tmpSchemaEntry.Column)
{
tmpHasDeletedParameter = true;
break;
}
}
}
if (!tmpHasDeletedParameter)
{
//if not, we need to add it
tmpFilter.push(
{
Column: tmpTableName + '.' + tmpSchemaEntry.Column,
Operator: '=',
Value: 0,
Connector: 'AND',
Parameter: 'Deleted'
});
}
break;
}
}
}
if (tmpFilter.length < 1)
{
return '';
}
var tmpWhere = ' WHERE';
// This is used to disable the connectors for subsequent queries.
// Only the open parenthesis operator uses this, currently.
var tmpLastOperatorNoConnector = false;
for (var i = 0; i < tmpFilter.length; i++)
{
if ((tmpFilter[i].Connector != 'NONE') && (tmpFilter[i].Operator != ')') && (tmpWhere != ' WHERE') && (tmpLastOperatorNoConnector == false))
{
tmpWhere += ' '+tmpFilter[i].Connector;
}
tmpLastOperatorNoConnector = false;
var tmpColumnParameter;
if (tmpFilter[i].Operator === '(')
{
// Open a logical grouping
tmpWhere += ' (';
tmpLastOperatorNoConnector = true;
}
else if (tmpFilter[i].Operator === ')')
{
// Close a logical grouping
tmpWhere += ' )';
}
else if (tmpFilter[i].Operator === 'IN')
{
tmpColumnParameter = tmpFilter[i].Parameter+'_w'+i;
// Add the column name, operator and parameter name to the list of where value parenthetical
tmpWhere += ' '+escapeColumn(tmpFilter[i].Column, pParameters)+' '+tmpFilter[i].Operator+' ( :'+tmpColumnParameter+' )';
pParameters.query.parameters[tmpColumnParameter] = tmpFilter[i].Value;
}
else if (tmpFilter[i].Operator === 'IS NOT NULL')
{
// IS NOT NULL is a special operator which doesn't require a value, or parameter
tmpWhere += ' '+escapeColumn(tmpFilter[i].Column, pParameters)+' '+tmpFilter[i].Operator;
}
else
{
tmpColumnParameter = tmpFilter[i].Parameter+'_w'+i;
// Add the column name, operator and parameter name to the list of where value parenthetical
tmpWhere += ' '+escapeColumn(tmpFilter[i].Column, pParameters)+' '+tmpFilter[i].Operator+' :'+tmpColumnParameter;
pParameters.query.parameters[tmpColumnParameter] = tmpFilter[i].Value;
}
}
return tmpWhere;
};
/**
* Generate an ORDER BY clause from the sort array
*
* Each entry in the sort is an object like:
* {Column:'Color',Direction:'Descending'}
*
* @method: generateOrderBy
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the field list clause
*/
var generateOrderBy = function(pParameters)
{
var tmpOrderBy = pParameters.sort;
if (!Array.isArray(tmpOrderBy) || tmpOrderBy.length < 1)
{
return '';
}
var tmpOrderClause = ' ORDER BY';
for (var i = 0; i < tmpOrderBy.length; i++)
{
if (i > 0)
{
tmpOrderClause += ',';
}
tmpOrderClause += ' '+escapeColumn(tmpOrderBy[i].Column, pParameters);
if (tmpOrderBy[i].Direction == 'Descending')
{
tmpOrderClause += ' DESC';
}
}
return tmpOrderClause;
};
/**
* Generate the limit clause
*
* @method: generateLimit
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateLimit = function(pParameters)
{
if (!pParameters.cap)
{
return '';
}
var tmpLimit = ' LIMIT';
// Cap is required for a limit clause.
tmpLimit += ' ' + pParameters.cap;
// If there is a begin record, we'll pass that in as well.
if (pParameters.begin !== false)
{
tmpLimit += ' FETCH ' + pParameters.begin;
}
return tmpLimit;
};
/**
* Generate the update SET clause
*
* @method: generateUpdateSetters
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateUpdateSetters = function(pParameters)
{
var tmpRecords = pParameters.query.records;
// We need to tell the query not to generate improperly if there are no values to set.
if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
{
return false;
}
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
var tmpUpdate = '';
// If there is more than one record in records, we are going to ignore them for now.
var tmpCurrentColumn = 0;
for(var tmpColumn in tmpRecords[0])
{
// No hash table yet, so, we will just linear search it for now.
// This uses the schema to decide if we want to treat a column differently on insert
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
for (var i = 0; i < tmpSchema.length; i++)
{
if (tmpColumn == tmpSchema[i].Column)
{
// There is a schema entry for it. Process it accordingly.
tmpSchemaEntry = tmpSchema[i];
break;
}
}
if (pParameters.query.disableAutoDateStamp &&
tmpSchemaEntry.Type === 'UpdateDate')
{
// This is ignored if flag is set
continue;
}
if (pParameters.query.disableAutoUserStamp &&
tmpSchemaEntry.Type === 'UpdateIDUser')
{
// This is ignored if flag is set
continue;
}
switch (tmpSchemaEntry.Type)
{
case 'AutoIdentity':
case 'CreateDate':
case 'CreateIDUser':
case 'DeleteDate':
case 'DeleteIDUser':
// These are all ignored on update
continue;
}
if (tmpCurrentColumn > 0)
{
tmpUpdate += ',';
}
switch (tmpSchemaEntry.Type)
{
case 'UpdateDate':
// This is an autoidentity, so we don't parameterize it and just pass in NULL
tmpUpdate += ' '+escapeColumn(tmpColumn, pParameters)+' = NOW()';
break;
case 'UpdateIDUser':
// This is the user ID, which we hope is in the query.
// This is how to deal with a normal column
var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
tmpUpdate += ' '+escapeColumn(tmpColumn, pParameters)+' = :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
break;
default:
var tmpColumnDefaultParameter = tmpColumn+'_'+tmpCurrentColumn;
tmpUpdate += ' '+escapeColumn(tmpColumn, pParameters)+' = :'+tmpColumnDefaultParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnDefaultParameter] = tmpRecords[0][tmpColumn];
break;
}
// We use a number to make sure parameters are unique.
tmpCurrentColumn++;
}
// We need to tell the query not to generate improperly if there are no values set.
if (tmpUpdate === '')
{
return false;
}
return tmpUpdate;
};
/**
* Generate the update-delete SET clause
*
* @method: generateUpdateDeleteSetters
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateUpdateDeleteSetters = function(pParameters)
{
if (pParameters.query.disableDeleteTracking)
{
//Don't generate an UPDATE query if Delete tracking is disabled
return false;
}
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
var tmpCurrentColumn = 0;
var tmpHasDeletedField = false;
var tmpUpdate = '';
// No hash table yet, so, we will just linear search it for now.
// This uses the schema to decide if we want to treat a column differently on insert
var tmpSchemaEntry = {Type:'Default'};
for (var i = 0; i < tmpSchema.length; i++)
{
// There is a schema entry for it. Process it accordingly.
tmpSchemaEntry = tmpSchema[i];
var tmpUpdateSql = null;
switch (tmpSchemaEntry.Type)
{
case 'Deleted':
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = 1';
tmpHasDeletedField = true; //this field is required in order for query to be built
break;
case 'DeleteDate':
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = NOW()';
break;
case 'UpdateDate':
// Delete operation is an Update, so we should stamp the update time
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = NOW()';
break;
case 'DeleteIDUser':
// This is the user ID, which we hope is in the query.
// This is how to deal with a normal column
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
break;
default:
//DON'T allow update of other fields in this query
continue;
}
if (tmpCurrentColumn > 0)
{
tmpUpdate += ',';
}
tmpUpdate += tmpUpdateSql;
// We use a number to make sure parameters are unique.
tmpCurrentColumn++;
}
// We need to tell the query not to generate improperly if there are no values set.
if (!tmpHasDeletedField ||
tmpUpdate === '')
{
return false;
}
return tmpUpdate;
};
/**
* Generate the update-delete SET clause
*
* @method: generateUpdateDeleteSetters
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateUpdateUndeleteSetters = function(pParameters)
{
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
var tmpCurrentColumn = 0;
var tmpHasDeletedField = false;
var tmpUpdate = '';
// No hash table yet, so, we will just linear search it for now.
// This uses the schema to decide if we want to treat a column differently on insert
var tmpSchemaEntry = {Type:'Default'};
for (var i = 0; i < tmpSchema.length; i++)
{
// There is a schema entry for it. Process it accordingly.
tmpSchemaEntry = tmpSchema[i];
var tmpUpdateSql = null;
switch (tmpSchemaEntry.Type)
{
case 'Deleted':
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = 0';
tmpHasDeletedField = true; //this field is required in order for query to be built
break;
case 'UpdateDate':
// Delete operation is an Update, so we should stamp the update time
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = NOW()';
break;
case 'UpdateIDUser':
// This is the user ID, which we hope is in the query.
// This is how to deal with a normal column
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
break;
default:
//DON'T allow update of other fields in this query
continue;
}
if (tmpCurrentColumn > 0)
{
tmpUpdate += ',';
}
tmpUpdate += tmpUpdateSql;
// We use a number to make sure parameters are unique.
tmpCurrentColumn++;
}
// We need to tell the query not to generate improperly if there are no values set.
if (!tmpHasDeletedField ||
tmpUpdate === '')
{
return false;
}
return tmpUpdate;
};
/**
* Generate the create SET clause
*
* @method: generateCreateSetList
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateCreateSetValues = function(pParameters)
{
var tmpRecords = pParameters.query.records;
// We need to tell the query not to generate improperly if there are no values to set.
if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
{
return false;
}
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
var tmpCreateSet = '';
// If there is more than one record in records, we are going to ignore them for now.
var tmpCurrentColumn = 0;
for(var tmpColumn in tmpRecords[0])
{
// No hash table yet, so, we will just linear search it for now.
// This uses the schema to decide if we want to treat a column differently on insert
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
for (var i = 0; i < tmpSchema.length; i++)
{
if (tmpColumn == tmpSchema[i].Column)
{
// There is a schema entry for it. Process it accordingly.
tmpSchemaEntry = tmpSchema[i];
break;
}
}
if (!pParameters.query.disableDeleteTracking)
{
if (tmpSchemaEntry.Type === 'DeleteDate' ||
tmpSchemaEntry.Type === 'DeleteIDUser')
{
// These are all ignored on insert (if delete tracking is enabled as normal)
continue;
}
}
if (tmpCurrentColumn > 0)
{
tmpCreateSet += ',';
}
//define a re-usable method for setting up field definitions in a default pattern
var buildDefaultDefinition = function()
{
var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
tmpCreateSet += ' :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = tmpRecords[0][tmpColumn];
};
var tmpColumnParameter;
switch (tmpSchemaEntry.Type)
{
case 'AutoIdentity':
if (pParameters.query.disableAutoIdentity)
{
buildDefaultDefinition();
}
else
{
// This is an autoidentity, so we don't parameterize it and just pass in NULL
tmpCreateSet += ' NULL';
}
break;
case 'AutoGUID':
if (pParameters.query.disableAutoIdentity)
{
buildDefaultDefinition();
}
else if (tmpRecords[0][tmpColumn] &&
tmpRecords[0][tmpColumn].length >= 5 &&
tmpRecords[0][tmpColumn] !== '0x0000000000000000') //stricture default
{
// Allow consumer to override AutoGUID
buildDefaultDefinition();
}
else
{
// This is an autoidentity, so we don't parameterize it and just pass in NULL
tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
tmpCreateSet += ' :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.UUID;
}
break;
case 'UpdateDate':
case 'CreateDate':
case 'DeleteDate':
if (pParameters.query.disableAutoDateStamp)
{
buildDefaultDefinition();
}
else
{
// This is an autoidentity, so we don't parameterize it and just pass in NULL
tmpCreateSet += ' NOW()';
}
break;
case 'UpdateIDUser':
case 'CreateIDUser':
case 'DeleteIDUser':
if (pParameters.query.disableAutoUserStamp)
{
buildDefaultDefinition();
}
else
{
// This is the user ID, which we hope is in the query.
// This is how to deal with a normal column
tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
tmpCreateSet += ' :'+tmpColumnParameter;
// Set the query parameter
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
}
break;
default:
buildDefaultDefinition();
break;
}
// We use an appended number to make sure parameters are unique.
tmpCurrentColumn++;
}
// We need to tell the query not to generate improperly if there are no values set.
if (tmpCreateSet === '')
{
return false;
}
return tmpCreateSet;
};
/**
* Generate the create SET clause
*
* @method: generateCreateSetList
* @param: {Object} pParameters SQL Query Parameters
* @return: {String} Returns the table name clause
*/
var generateCreateSetList = function(pParameters)
{
// The records were already validated by generateCreateSetValues
var tmpRecords = pParameters.query.records;
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
var tmpCreateSet = '';
// If there is more than one record in records, we are going to ignore them for now.
for(var tmpColumn in tmpRecords[0])
{
// No hash table yet, so, we will just linear search it for now.
// This uses the schema to decide if we want to treat a column differently on insert
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
for (var i = 0; i < tmpSchema.length; i++)
{
if (tmpColumn == tmpSchema[i].Column)
{
// There is a schema entry for it. Process it accordingly.
tmpSchemaEntry = tmpSchema[i];
break;
}
}
if (!pParameters.query.disableDeleteTracking)
{
if (tmpSchemaEntry.Type === 'DeleteDate' ||
tmpSchemaEntry.Type === 'DeleteIDUser')
{
// These are all ignored on insert (if delete tracking is enabled as normal)
continue;
}
}
switch (tmpSchemaEntry.Type)
{
default:
if (tmpCreateSet != '')
{
tmpCreateSet += ',';
}
tmpCreateSet += ' '+escapeColumn(tmpColumn, pParameters);
break;
}
}
return tmpCreateSet;
};
var Create = function(pParameters)
{
var tmpTableName = generateTableName(pParameters);
var tmpCreateSetList = generateCreateSetList(pParameters);
var tmpCreateSetValues = generateCreateSetValues(pParameters);
if (!tmpCreateSetValues)
{
return false;
}
return 'INSERT INTO'+tmpTableName+' ('+tmpCreateSetList+') VALUES ('+tmpCreateSetValues+');';
};
/**
* Read one or many records
*
* Some examples:
* SELECT * FROM WIDGETS;
* SELECT * FROM WIDGETS LIMIT 0, 20;
* SELECT * FROM WIDGETS LIMIT 5, 20;
* SELECT ID, Name, Cost FROM WIDGETS LIMIT 5, 20;
* SELECT ID, Name, Cost FROM WIDGETS LIMIT 5, 20 WHERE LastName = 'Smith';
*
* @method Read
* @param {Object} pParameters SQL Query parameters
* @return {String} Returns the current Query for chaining.
*/
var Read = function(pParameters)
{
var tmpFieldList = generateFieldList(pParameters);
var tmpTableName = generateTableName(pParameters);
var tmpWhere = generateWhere(pParameters);
var tmpOrderBy = generateOrderBy(pParameters);
var tmpLimit = generateLimit(pParameters);
const tmpOptDistinct = pParameters.distinct ? ' DISTINCT' : '';
if (pParameters.queryOverride)
{
try
{
var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
return tmpQueryTemplate({FieldList:tmpFieldList, TableName:tmpTableName, Where:tmpWhere, OrderBy:tmpOrderBy, Limit:tmpLimit, Distinct: tmpOptDistinct, _Params: pParameters});
}
catch (pError)
{
// This pokemon is here to give us a convenient way of not throwing up totally if the query fails.
console.log('Error with custom Read Query ['+pParameters.queryOverride+']: '+pError);
return false;
}
}
return `SELECT${tmpOptDistinct}${tmpFieldList} FROM${tmpTableName}${tmpWhere}${tmpOrderBy}${tmpLimit};`;
};
var Update = function(pParameters)
{
var tmpTableName = generateTableName(pParameters);
var tmpWhere = generateWhere(pParameters);
var tmpUpdateSetters = generateUpdateSetters(pParameters);
if (!tmpUpdateSetters)
{
return false;
}
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateSetters+tmpWhere+';';
};
var Delete = function(pParameters)
{
var tmpTableName = generateTableName(pParameters);
var tmpWhere = generateWhere(pParameters);
var tmpUpdateDeleteSetters = generateUpdateDeleteSetters(pParameters);
if (tmpUpdateDeleteSetters)
{
//If it has a deleted bit, update it instead of actually deleting the record
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateDeleteSetters+tmpWhere+';';
}
else
{
return 'DELETE FROM'+tmpTableName+tmpWhere+';';
}
};
var Undelete = function(pParameters)
{
var tmpTableName = generateTableName(pParameters);
let tmpDeleteTrackingState = pParameters.query.disableDeleteTracking;
pParameters.query.disableDeleteTracking = true;
var tmpWhere = generateWhere(pParameters);
var tmpUpdateUndeleteSetters = generateUpdateUndeleteSetters(pParameters);
pParameters.query.disableDeleteTracking = tmpDeleteTrackingState;
if (tmpUpdateUndeleteSetters)
{
//If it has a deleted bit, update it instead of actually deleting the record
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateUndeleteSetters+tmpWhere+';';
}
else
{
return 'SELECT NULL;';
}
};
var Count = function(pParameters)
{
var tmpTableName = generateTableName(pParameters);
var tmpWhere = generateWhere(pParameters);
const tmpFieldList = pParameters.distinct ? generateFieldList(pParameters, true) : '*';
// here, we ignore the distinct keyword if no fields have been specified and
if (pParameters.distinct && tmpFieldList.length < 1)
{
console.warn('Distinct requested but no field list or schema are available, so not honoring distinct for count query.');
}
const tmpOptDistinct = pParameters.distinct && tmpFieldList.length > 0 ? 'DISTINCT' : '';
if (pParameters.queryOverride)
{
try
{
var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
return tmpQueryTemplate({FieldList:[], TableName:tmpTableName, Where:tmpWhere, OrderBy:'', Limit:'', Distinct: tmpOptDistinct, _Params: pParameters});
}
catch (pError)
{
// This pokemon is here to give us a convenient way of not throwing up totally if the query fails.
console.log('Error with custom Count Query ['+pParameters.queryOverride+']: '+pError);
return false;
}
}
return `SELECT COUNT(${tmpOptDistinct}${tmpFieldList || '*'}) AS RowCount FROM${tmpTableName}${tmpWhere};`;
};
var tmpDialect = ({
Create: Create,
Read: Read,
Update: Update,
Delete: Delete,
Undelete: Undelete,
Count: Count
});
/**
* Dialect Name
*
* @property name
* @type string
*/
Object.defineProperty(tmpDialect, 'name',
{
get: function() { return 'ALASQL'; },
enumerable: true
});
return tmpDialect;
};
module.exports = FoxHoundDialectALASQL;