Zeldus

parseWhere

function
parseWhere() ->Object

Option name Type Description
sourceTable Object the name of the source table
query Object the query to parse

parse where clause

function parseWhere(sourceTable, query) {
		if(query === undefined)
			return null;

		var logicalOperators = ['and', 'or'],
			comparisonOperators = ['=', '>', '<', '>=', '<=', '<>', '!='],
			comparisonOperatorNames = ['E', 'GT', 'LT', 'GTE', 'LTE', 'NE', 'NE'];

		//first split out logical operators
		var logicalSplit = query.split(new RegExp('( ' + logicalOperators.join(' | ') + ' )'));
		logicalSplit = logicalSplit.map(function(l){ return l.trim(); });

		var predicates = [],
			logicalOperator = { type: null, children: [] };
		for(var i = 0; i < logicalSplit.length; i++){
			if(logicalOperators.indexOf(logicalSplit[i].toLowerCase()) < 0){
				var comparisonSplit = logicalSplit[i].split(new RegExp('(' + comparisonOperators.join('|') + ')')),
					predicate = { value: null, type: null };
				comparisonSplit = comparisonSplit.map(function(l){ return l.trim(); });

				if(comparisonSplit.length === 3){
					//predicate
					var comparisonIndex = comparisonOperators.indexOf(comparisonSplit[1]);
					if(comparisonIndex >= 0)
						predicate.type = comparisonOperatorNames[comparisonIndex];
					//value
					predicate.value = parseValue(comparisonSplit[2]);
					//data source
					predicates.push({ type: 'ds', table: sourceTable, column: comparisonSplit[0], predicate: predicate });
				}
			}else{
				//logical operator
				logicalOperator.type = logicalSplit[i];
			}
		}
		if(predicates.length <= 1)
			return predicates;
		else if(predicates.length > 1 && logicalOperator.type != null){
			//set logical operator
			logicalOperator.children = predicates;
			return logicalOperator;
		}else
			return null;
	}

parseSelect

function
parseSelect() ->Object

Option name Type Description
sourceTable Object the name of the source table
query Object the query to parse

parse select clause

function parseSelect(sourceTable, query) {
		var columns = query.split(','),
			select = { type: 'select', filter: [], children: [] };
		//remove leading/trailing whitespace
		columns = columns.map(function(c){ return c.trim().toLowerCase(); });
		for(var i = 0; i < columns.length; i++){
			select.children.push({ type: 'ds', table: sourceTable, column: columns[i] });
		}
		return select;
	}

parseValue

function
parseValue() ->Object

Option name Type Description
value Object the value to parse

identifies a data type, checks for number, date then string

function parseValue(value){
		//int or decimal?
		var number = +value;
		if(!isNaN(number))
			return number;

		//date?
		var date = Date.parse(value);
		if(!isNaN(date))
			return number;

		//string
		return value;
	}

QueryParser

function
QueryParser()

Query Parser
Parses a SQL query into an execution plan - a tree of operators

function QueryParser() {
		var self = this,
			keywords = ['select', 'from', 'where', 'sum', 'count', 'join'];

parse

method
self.parse() ->Object

Option name Type Description
query Object an SQL query

Parse

self.parse = function(query){
  • SQL **
    select L_SHIPMODE, sum(L_EXTENDEDPRICE)
    from LINEITEM
    where
    L_SUPPKEY = 777
    and convert(varchar(20), L_SHIPDATE, 112) > 19961101
    group by L_SHIPMODE

  • Manual Execution Plan
    //aggregate on shipmode and extendedprice
    var result = new ResultPrinter(
    new SumOperator(
    new AndOperator(
    //filter on shipdate and supplier key
    new DataSourceOperator(table.shipdate, new Predicate(19961101, 'GT')),
    new vOperator(table.supplierkey, new Predicate(777, 'E'))
    ),
    new DataSourceOperator(table.shipmode),
    new DataSourceOperator(table.extendedprice)
    ),
    ['shipmode', 'extendedprice']
    );

//execution plan
			var executionPlan = {
				type: '',
				filter: [],
				children: []
			};

			//tidy up
			query = query.replace(/(\r\n|\n|\r)/gm, '');
			//split stirng into keywords
			var tokens = query.split(new RegExp('(' + keywords.join('|') + ')'));
			var currentKeyword = null,
				parts = {};
			for(var i = 0; i < tokens.length; i++){
				if(tokens[i].length > 0){
					if(keywords.indexOf(tokens[i]) >= 0)
						currentKeyword = tokens[i];
					else{
						parts[currentKeyword] = tokens[i].trim();
						currentKeyword = null;
					}
				}
			}

			//from - determine source table
			var sourceTable = parts.from;

			//where
			var where = parseWhere(sourceTable, parts.where);

			//select
			var select = parseSelect(sourceTable, parts.select);

			if(where != null){
				//where has a logical operator
				if(where.type !== undefined)
					select.filter.push(where);
				//where has child data source operators
				else if(where.length > 0){
					//set as either filter or children
					for(var i = 0; i < where.length; i++){
						var found = false,
							j = 0;
						for(; j < select.children.length; j++){
							if(select.children[j].table === where[i].table && select.children[j].column === where[i].column){
								found = true;
								break;
							}
						}
						//if the perdicate (where) is part of the selected columns, then add it as a child data source
						if(found)
							select.children[j] = where[i];
						else
						//else add the predicate as a filter
							select.filter.push(where[i]);
					}
				}
			}

			return select;

return {
type: 'sum',
filter: [
{
type: 'and',
children: [
{
type: 'ds',
table: 'lineitem',
column: 'shipdate',
predicate: { value: 19961101, type: 'GT' }
},
{
type: 'ds',
table: 'lineitem',
column: 'lineitem.supplierkey',
predicate: { value: 777, type: 'E' }
}
]
}
],
children: [
{
type: 'ds',
table: 'lineitem',
column: 'shipmode'
},
{
type: 'ds',
table: 'lineitem',
column: 'extendedprice'
}
]
};

};
	}
	return QueryParser;
});