@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
1,202 lines (1,050 loc) • 35.8 kB
JavaScript
'use strict';
//Includes
var NotSupported = require('./errors/http/notSupported');
var uriToDb = require('./../utils/typeConverters/uriToDb').uriToDb;
var Type_Error = require('./errors/typeError');
//Supported expression kinds
exports.NODE_LITERAL = 1;//TODO
exports.NODE_LITERAL_NUMBER = 2;
exports.NODE_LITERAL_DATETIME = 3;
exports.NODE_LITERAL_TIME = 4;
exports.NODE_LITERAL_BOOLEAN = 5;
exports.NODE_LITERAL_GUID = 6;
exports.NODE_LITERAL_BINARY = 7;
exports.NODE_LITERAL_STRING = 8;
exports.NODE_LITERAL_NULL = 9;
exports.NODE_LITERAL_NIL = 10;
exports.NODE_LITERAL_MAX_VALUE = 19;
exports.NODE_BINARY = 20;
exports.NODE_NEGATE = 21;
exports.NODE_UNARY = 22;
exports.NODE_METHOD = 23;
exports.NODE_PARENTHESIS = 24;
exports.NODE_MEMBER = 25;
exports.NODE_PROPERTY = 26;
//
exports.NODE_SORTORDER = 27;
exports.NODE_ORDERBY = 28;
//Supported binary operators
exports.OR = 0;
exports.AND = 1;
exports.EQ = 2;
exports.NE = 3;
exports.GE = 4;
exports.LE = 5;
exports.GT = 6;
exports.LT = 7;
exports.ADD = 8;
exports.SUB = 9;
exports.MUL = 10;
exports.DIV = 11;
exports.MOD = 12;
exports.binarySqlMapping = [
' or ', ' and ', ' = ', ' != ',
' >= ', ' <= ', ' > ', ' < ',
' + ', ' - ', ' * ', ' / ',
' mod '
];
//Supported unary operators
exports.NOT = 0;
exports.NEGATE = 1;
exports.unarySqlMapping = [
'not ', '-'
];
//Supported methods
exports.STARTSWITH = 'startswith';
exports.ENDSWITH = 'endswith';
exports.SUBSTRING = 'substring';
exports.SUBSTRINGOF = 'substringof';
exports.INDEXOF = 'indexof';
exports.REPLACE = 'replace';
exports.TOLOWER = 'tolower';
exports.TOUPPER = 'toupper';
exports.TRIM = 'trim';
exports.CONCAT = 'concat';
exports.LENGTH = 'length';
exports.YEAR = 'year';
exports.MONTH = 'month';
exports.DAY = 'day';
exports.HOUR = 'hour';
exports.MINUTE = 'minute';
exports.SECOND = 'second';
exports.ROUND = 'round';
exports.CEILING = 'ceiling';
exports.FLOOR = 'floor';
exports.Number = ONumber;
exports.DateTime = DateTime;
exports.Time = Time;
exports.Binary = Binary;
exports.Guid = Guid;
exports.Boolean = OBoolean;
exports.EdmString = EdmString;
exports.BinaryOperator = BinaryOperator;
exports.UnaryOperator = UnaryOperator;
exports.Parenthesis = Parenthesis;
exports.Method = Method;
exports.Member = Member;
exports.Property = Property;
//exports.Literal = Literal;
exports.SortOrder = SortOrder;
exports.OrderBy = OrderBy;
exports.Null = Null;
exports.Nil = Nil;
exports.DbValue = DbValue;
exports.cloneContextWithNoAlias = cloneContextWithNoAlias;
//Code
function DbValue(value, dbType) {
this.value = value;
this.dbType = dbType;
}
DbValue.prototype.toSqlHana = function toSqlHana(context, parameter) {
if (this.value.toSqlHana) {
if (this.dbType) {
return this.value.toSqlHana(context, parameter, { useDbType: this.dbType });
} else {
return this.value.toSqlHana(context, parameter);
}
} else {
parameter.push(this.value);
return '?';
}
};
DbValue.prototype.getChildren = function () {
return [];
};
/** Edm.Time representation
* @class Time
* Tested via test_filter_parser.js
*/
function Time(value) {
this.kind = exports.NODE_LITERAL_TIME;
this.value = value;
}
/** Conversion to HANA db type Secondtime
* Sample from HANA xsengine logger file:
* Secondtime, value: 11:12:13
*/
Time.prototype.toSqlHana = function toSqlHana(context, parameter) {
let value = uriToDb['Edm.Time'](this.value, 'TIME');
parameter.push(value);
return '?';
};
Time.prototype.getChildren = function () {
return [];
};
/** Edm.Time representation
* @class Time
* Tested via test_filter_parser.js
*/
function Nil() {
this.kind = exports.NODE_LITERAL_NIL;
}
/** Conversion to HANA db type Secondtime
* Sample from HANA xsengine logger file:
* Secondtime, value: 11:12:13
*/
Nil.prototype.toSqlHana = function toSqlHana() {
throw new Type_Error('Nil can not converted to HANA sql syntax');
};
Nil.prototype.getChildren = function () {
return [];
};
/** Edm.Time representation
* @class Time
* Tested via test_filter_parser.js
*/
function Null() {
this.kind = exports.NODE_LITERAL_NULL;
}
/** Conversion to HANA db type Secondtime
* Sample from HANA xsengine logger file:
* Secondtime, value: 11:12:13
*/
Null.prototype.toSqlHana = function toSqlHana() {
return 'null';
};
Null.prototype.getChildren = function () {
return [];
};
/** Edm.Time representation
* @class DateTime
* Tested via test_filter_parser.js
*/
function DateTime(value) {
this.kind = exports.NODE_LITERAL_DATETIME;
this.value = value;
}
/** Conversion to HANA db type Secondtime
* Sample from HANA xsengine logger file:
* Longdate, value: 1999-01-02 03:45:00.0000000
*/
DateTime.prototype.toSqlHana = function (context, parameter, hint) {
if (hint && hint.useDbType) {
let value = uriToDb['Edm.DateTime'](this.value, hint.useDbType);
parameter.push(value);
} else {
let value = uriToDb['Edm.DateTime'](this.value, 'TIMESTAMP');
parameter.push(value);
}
return '?';
};
DateTime.prototype.getChildren = function () {
return [];
};
/** Edm.Byte, Edm.Int16, Edm.Int32, Edm.Int64, Edm.Decimal, Edm.Single, Edm.Double representation
* @class ONumber
* @param {string} number e.g. >1.552<
* Tested via test_filter_parser.js
*/
function ONumber(number/*intPart, floatPart, exp, postFix*/) {
this.kind = exports.NODE_LITERAL_NUMBER;
this.uriNumber = number;
this.preNumber = undefined;
let type = number.substr(number.length - 1).toUpperCase();
if (type === 'F' ||
type === 'f' ||
type === 'D' ||
type === 'd' ||
type === 'M' ||
type === 'm' ||
type === 'L' ||
type === 'l') {
number = number.substring(0, number.length - 1);
this.type = type;
} else {
this.type = null;
}
this.sign = 1;// > 0
let exponent;
if (number.indexOf('E') !== -1) {
exponent = number.split('E');
} else if (number.indexOf('e') !== -1) {
exponent = number.split('e');
} else {
exponent = [number];
}
let intFloat = exponent[0].split('.');
this.i = intFloat[0] || null;
this.f = intFloat[1] || null;
this.e = exponent[1] || null;
this.preNumber = '';
//this.preNumber += sign;
if (this.i) {
this.preNumber += this.i;
} else {
this.preNumber += 0;
}
if (this.f) {
this.preNumber += '.' + this.f;
}
if (this.e) {
this.preNumber += 'e' + this.e;
}
}
/** Conversion to HANA db type Secondtime
* Sample fro
* HANA xsengine logger file:
* Integer, value: 1
* BigInt, value: 4294836224
* BigInt, value: -4294836224
*/
ONumber.prototype.toSqlHana = function toSqlHana(context, parameter) {
let input;
if (this.sign === 1) {
input = this.preNumber;
} else {
input = '-' + this.preNumber;
}
let dbValue;
if (this.type === null) {
//simple TINYINT(Edm.Byte),SMALLINT(edm.Int16),INTEGER(Edm.Int32) all over Edm.Int32
dbValue = parseInt(input);
} else if (this.type === 'F') {
dbValue = parseFloat(input);
} else if (this.type === 'D') {
dbValue = parseFloat(input);
} else if (this.type === 'M') {
dbValue = input;
} else if (this.type === 'L') {
dbValue = input;
} else {
throw new Type_Error(this.type);
}
parameter.push(dbValue);
return '?';
};
ONumber.prototype.getChildren = function () {
return [];
};
/** Edm.String representation
* @class EdmString
* Tested via test_filter_parser.js
*/
function EdmString(value) {
this.kind = exports.NODE_LITERAL_STRING;
this.string = value;
//this.string = value.substring(1,value.length-1).replace(reg2to1Quote,'\'');
}
/** Conversion to HANA db type NString
* Sample from HANA xsengine logger file:
* NString, value: 'st r''ing'
*/
EdmString.prototype.toSqlHana = function toSqlHana(context, parameter) {
let value = uriToDb['Edm.String'](this.string, 'VARCHAR');
parameter.push(value);
return '?';
};
EdmString.prototype.getChildren = function () {
return [];
};
//var reg2to1Quote = new RegExp('\'\'','g');
/** Edm.Binary representation
* @class Binary
* Tested via test_filter_parser.js
*/
function Binary(value) {
this.kind = exports.NODE_LITERAL_BINARY;
this.value = value;
}
/** Conversion to HANA db type BString
* Sample from HANA xsengine logger file:
* BString, value: x'dcba00'
*/
Binary.prototype.toSqlHana = function toSqlHana(context, parameter) {
let value = uriToDb['Edm.Binary'](this.value, 'VARBINARY');
parameter.push(value);
//parameter.push('x\'' + this.value + '\'');
return '?';
};
Binary.prototype.getChildren = function () {
return [];
};
/** Edm.Binary representation
* @class Guid
* Tested via test_filter_parser.js
*/
function Guid(value) {
this.kind = exports.NODE_LITERAL_GUID;
this.guid = value;
}
Guid.prototype.toSqlHana = function toSqlHana(/*context*/) {
throw new NotSupported('GUID ODataType not supporeted');
};
Guid.prototype.getChildren = function () {
return [];
};
function OBoolean(value) {
this.kind = exports.NODE_LITERAL_BOOLEAN;
this.value = value;
}
OBoolean.prototype.toSqlHana = function toSqlHana(/*context, parameter*/) {
//see ResourceHandling/OData/SqlProcessor.cpp
//Method sqlProcessor::serializeExpr case Expr::LITERAL
//But be aware the $filter= true eq true
//becomes WHERE (1=1)=(1=1)
//and will lead to an SQL syntax error
if (this.value) {
return '(1=1)';
} else {
return '(1=0)';
}
};
OBoolean.prototype.getChildren = function () {
return [];
};
/** BinaryOperator
* @class BinaryOperator
* Tested via test_filter_parser.js
* @param {Number} op (e.g. exports.AND)
* @param left
* @param right
* @param strict Use direct "left=right" in sql generation, not the lax OData version like "(left=right) or (( left is null) and (right is null))"
* @constructor
*/
function BinaryOperator(op, left, right, strict) {
this.kind = exports.NODE_BINARY;
this.op = op;
this.left = left;
this.right = right;
this.strict = strict; // use strict = false if null is supported
}
BinaryOperator.prototype.setAlias = function setAlias(alias) {
if (this.left.setAlias) {
this.left.setAlias(alias);
}
if (this.right.setAlias) {
this.right.setAlias(alias);
}
return '?';
};
BinaryOperator.prototype.applyConverter = createApplyConverter('left', 'right');
BinaryOperator.prototype.getChildren = function () {
return [this.left, this.right];
};
function isLiteral(node) {
return 0 < node.kind && node.kind < exports.NODE_LITERAL_MAX_VALUE;
}
function isNull(node) {
return node.kind === exports.NODE_LITERAL_NULL;
}
//see ResourceHandling/OData/SqlProcessor.cpp
function complexBinaryWithNull(first, op, second, context, parameter) {
//here only if null is supported
let literal = 0;
let _null = false;
let tmp;
let sql = '';
let pLeft = [];
let pRight = [];
if (isLiteral(second)) {
literal++;
if (isNull(second)) {
_null = true;
}
}
if (isLiteral(first)) {
literal++;
if (!_null) {
if (isNull(first)) {
_null = true;
//first should be property or Literal; second should be null
tmp = second;
second = first;
first = tmp;
} else if (literal === 1) {
//first should be property, second should be literal
tmp = second;
second = first;
first = tmp;
}
}
}
let left = first.toSqlHana(context, pLeft);
let right = second.toSqlHana(context, pRight);
if (literal === 0) {
if (context.isNav(first.property) && context.isNav(second.property)) {
if (op === exports.EQ) {
throw new Error("Operator 'eq' incompatible with operand types '<Collection>' and '<Collection>'.");
} else {
throw new Error("Operator 'ne' incompatible with operand types '<Collection>' and '<Collection>'.");
}
}
//only properties are used
if (op === exports.EQ) {
//equals
//out << "(";
//serializeExpr(f, out);
//out << "=";
//serializeExpr(s, out);
//out << " or (";
//serializeExpr(f, out);
//out << " is null and ";
//serializeExpr(s, out);
//out << " is null))";
sql += '(' + left + ' = ' + right +
' or (' + left + ' is null and ' + right + ' is null))';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
} else {
//not equals
//out << "((";
//serializeExpr(f, out);
//out << "!=";
//serializeExpr(s, out);
//out << " and not (";
//serializeExpr(f, out);
//out << " is null and ";
//serializeExpr(s, out);
//out << " is null)) or (";
//serializeExpr(f, out);
//out << " is null and ";
//serializeExpr(s, out);
//out << " is not null) or (";
//serializeExpr(f, out);
//out << " is not null and ";
//serializeExpr(s, out);
//out << " is null))";
sql +=
'((' + left + ' != ' + right +
' and not (' + left + ' is null and ' + right + ' is null))' +
' or (' + left + ' is null and ' + right + ' is not null)' +
' or (' + left + ' is not null and ' + right + ' is null))';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
}
} else if (literal === 1) {
if (context.isNav(first.property)) {
if (_null) {
//TODO add feature
throw new NotSupported("Comparing operand type '<Collection>' with null not supported.", 406);
//out << (type == Expr::EQ ? " not exists (select 1 from " : " exists (select 1 from");
//serializeSegment(ast::cast<const ast::Member>(f)->lastSegment(), out);
//out << ")";
//if (op === exports.EQ) {
// sql += '( not exists (select 1 from ' + '...' + ')';
//} else {
// sql += 'exists (select 1 from ' + '...' + ')';
//}
} else {
if (op === exports.EQ) {
throw new Error("Operator 'eq' incompatible with operand types '<Collection>' and non '<Collection>'.");
} else {
throw new Error("Operator 'ne' incompatible with operand types '<Collection>' and non '<Collection>'.");
}
}
} else {
if (op === exports.EQ) {
if (_null) {
//serializeExpr(f, out);
//out << ( " is ");
//serializeExpr(s, out);
sql += '(' + left + ' is ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
} else {
//serializeExpr(f, out);
//out << ( " = ");
//serializeExpr(s, out);
sql += '(' + left + ' = ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
}
} else if (op === exports.NE) {
if (_null) {
//serializeExpr(f, out);
//out << ( " is not ");
//serializeExpr(s, out);
sql += '(' + left + ' is not ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
} else {
//out << "(";
//serializeExpr(f, out);
//out << "!=";
//serializeExpr(s, out);
//out << " or ";
//serializeExpr(f, out);
//out << " is null)";
sql += '(' + left + ' != ' + right + ' or ' + left + ' is null)';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
parameter.push.apply(parameter, pLeft);
}
}
}
}
else if (literal === 2) {
if (op === exports.EQ) {
//serializeExpr(f, out);
//out << (!null ? strings[type] : (" is " );
//serializeExpr(s, out);
if (_null) {
sql += '(' + left + ' is ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
} else {
sql += '(' + left + ' = ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
}
} else {
//serializeExpr(f, out);
//out << (!null ? strings[type] : ( " is not ";
//serializeExpr(s, out);
if (_null) {
sql += '(' + left + ' is not ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
} else {
sql += '(' + left + ' != ' + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
}
}
}
return sql;
}
BinaryOperator.prototype.getTypeHint = function () {
if (this.left.kind === exports.NODE_PROPERTY) {
return {
applyOn: 'right',
dbType: this.left.dbType
};
} else if (this.right.kind === exports.NODE_PROPERTY) {
return {
applyOn: 'left',
dbType: this.right.dbType
};
}
return null;
};
BinaryOperator.prototype.toSqlHana = function toSqlHana(context, parameter) {
this.kind = exports.NODE_BINARY;
let pLeft = [];
let pRight = [];
let castType = this.getTypeHint();
let left = null;
let right = null;
if (castType) {
if (castType.applyOn === 'left') {
left = this.left.toSqlHana(context, pLeft, { useDbType: castType.dbType });
right = this.right.toSqlHana(context, pRight);
}
if (castType.applyOn === 'right') {
left = this.left.toSqlHana(context, pLeft);
right = this.right.toSqlHana(context, pRight, { useDbType: castType.dbType });
}
} else {
left = this.left.toSqlHana(context, pLeft);
right = this.right.toSqlHana(context, pRight);
}
let sql = '';
switch (this.op) {
case exports.EQ :
if (this.strict || context.oDataNullSupport !== true) {
sql += '(' + left + exports.binarySqlMapping[this.op] + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
break;
}
sql += complexBinaryWithNull(this.left, this.op, this.right, context, parameter);
break;
case exports.NE : //special case
if (this.strict || context.oDataNullSupport !== true) {
sql += '(' + left + exports.binarySqlMapping[this.op] + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
break;
}
sql += complexBinaryWithNull(this.left, this.op, this.right, context, parameter);
break;
case exports.OR :
case exports.AND :
case exports.GE :
case exports.LE :
case exports.GT :
case exports.LT :
case exports.ADD :
case exports.SUB :
case exports.MUL :
case exports.DIV :
sql += '(' + left + exports.binarySqlMapping[this.op] + right + ')';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
break;
case exports.MOD :
sql += '(mod(' + left + ',' + right + '))';
parameter.push.apply(parameter, pLeft);
parameter.push.apply(parameter, pRight);
break;
}
return sql;
};
/** UnaryOperator
* @class UnaryOperator
* Tested via test_filter_parser.js
*/
function UnaryOperator(op, value) {
this.kind = exports.NODE_UNARY;
this.op = op;
this.value = value;
}
UnaryOperator.prototype.toSqlHana = function toSqlHana(context, parameter) {
let p = [];
let value = this.value.toSqlHana(context, p);
parameter.push.apply(parameter, p);
return '(' + exports.unarySqlMapping[this.op] + value + ')';
};
UnaryOperator.prototype.setAlias = function (alias) {
if (this.value.setAlias) {
this.value.setAlias(alias);
}
};
UnaryOperator.prototype.applyConverter = createApplyConverter('value');
UnaryOperator.prototype.getChildren = function () {
return [this.value];
};
/** Method
* @class UnaryOperator
* Tested via test_filter_parser.js
*/
function Method(method, parameter) {
this.kind = exports.NODE_METHOD;
this.method = method;
this.parameter = parameter;
}
Method.prototype.setAlias = function (alias) {
for (let i = 0; i < this.parameter.length; i++) {
if (this.parameter[i].setAlias) {
this.parameter[i].setAlias(alias);
}
}
};
Method.prototype.applyConverter = createApplyConverter('parameter');
Method.prototype.getChildren = function () {
return this.parameter;
};
Method.prototype.toSqlHana = function toSqlHana(context, parameter) {
let sql = '';
let p0 = [];
let p1 = [];
let p2 = [];
// SAP HANA SQL and System Views Reference
// LIKE Predicate at: https://help.sap.com/viewer/4fe29514fd584807ac9f2a04f6754767/2.0.02/en-US/20fa17f375191014a4d8d8cbfddfe340.html
let inP0;
let inP1;
switch (this.method) {
case exports.STARTSWITH :
//start
sql = '(' + this.parameter[0].toSqlHana(context, p0);
sql += ' LIKE ';
inP1 = this.parameter[1];
if (inP1.kind === exports.NODE_LITERAL_STRING) {
if ((!inP1.string.includes('_') && !inP1.string.includes('%'))) {
// no escaping of the pattern required
sql += 'concat(' + inP1.toSqlHana(context, p1) + ",'%')";
sql += ')'; //end
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
}
}
sql += 'concat(replace(replace(replace(';
sql += inP1.toSqlHana(context, p1);
sql += ",'\\','\\\\'),'%','\\%'),'_','\\_'),'%')";
sql += " escape '\\'";
sql += ')'; //end
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
case exports.ENDSWITH :
sql = '(' + this.parameter[0].toSqlHana(context, p0);
sql += ' LIKE ';
inP1 = this.parameter[1];
if (inP1.kind === exports.NODE_LITERAL_STRING) {
if ((!inP1.string.includes('_') && !inP1.string.includes('%'))) {
// no escaping of the pattern required
sql += "concat('%'," + inP1.toSqlHana(context, p1) + ")";
sql += ')'; //end
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
}
}
sql += "concat('%', replace(replace(replace(";
sql += inP1.toSqlHana(context, p1);
sql += ",'\\','\\\\'),'%','\\%'),'_','\\_')";
sql += ")";
sql += " escape '\\'";
sql += ')'; //end
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
case exports.SUBSTRING :
if (this.parameter[2]) {
sql = '' + 'SUBSTRING(' + this.parameter[0].toSqlHana(context, p0);
sql += ',' + this.parameter[1].toSqlHana(context, p1) + '+1';
sql += ',' + this.parameter[2].toSqlHana(context, p2) + ')';
sql += '';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
parameter.push.apply(parameter, p2);
} else {
sql = '' + 'SUBSTRING(' + this.parameter[0].toSqlHana(context, p0);
sql += ',' + this.parameter[1].toSqlHana(context, p1) + '+1' + ')';
sql += '';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
}
break;
case exports.SUBSTRINGOF :
//true if the first parameter is SUBSTRINGOf the second parameter
sql = '(' + this.parameter[1].toSqlHana(context, p0);
sql += ' LIKE ';
inP0 = this.parameter[0];
if (inP0.kind === exports.NODE_LITERAL_STRING) {
if ((!inP0.string.includes('_') && !inP0.string.includes('%'))) {
// no escaping of the pattern required
sql += 'concat(\'%\',' +
'concat(' + inP0.toSqlHana(context, p1) + ',\'%\')' +
')';
sql += ')';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
}
}
sql += "concat('%',concat(replace(replace(replace(";
sql += inP0.toSqlHana(context, p1);
sql += ",'\\','\\\\'),'%','\\%'),'_','\\_')";
sql += ",'%'))";
sql += " escape '\\'";
sql += ')';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
case exports.INDEXOF :
sql = '(-1)+LOCATE(';
sql += this.parameter[0].toSqlHana(context, p0) + ',';
sql += this.parameter[1].toSqlHana(context, p1) + ')';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
case exports.REPLACE :
sql = 'REPLACE(';
sql += this.parameter[0].toSqlHana(context, p0) + ',';
sql += this.parameter[1].toSqlHana(context, p1) + ',';
sql += this.parameter[2].toSqlHana(context, p2) + ')';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
parameter.push.apply(parameter, p2);
break;
case exports.TOLOWER :
sql = 'LCASE(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.TOUPPER :
sql = 'UCASE(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.TRIM :
sql = 'TRIM(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.CONCAT :
sql = 'CONCAT(';
sql += this.parameter[0].toSqlHana(context, p0) + ',';
sql += this.parameter[1].toSqlHana(context, p1) + ')';
parameter.push.apply(parameter, p0);
parameter.push.apply(parameter, p1);
break;
case exports.LENGTH :
sql = 'LENGTH(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.YEAR :
sql = 'YEAR(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.MONTH :
sql = 'MONTH(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.DAY :
sql = 'DAYOFMONTH(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.HOUR :
sql = 'HOUR(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.MINUTE :
sql = 'MINUTE(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.SECOND :
sql = 'SECOND(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.ROUND :
sql = 'ROUND(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.CEILING :
sql = 'CEIL(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
case exports.FLOOR :
sql = 'FLOOR(';
sql += this.parameter[0].toSqlHana(context, p0) + ')';
parameter.push.apply(parameter, p0);
break;
}
return sql;
};
/** Parenthesis
* @class Parenthesis
* Tested via test_filter_parser.js
*/
function Parenthesis(value) {
this.kind = exports.NODE_PARENTHESIS;
this.value = value;
}
Parenthesis.prototype.toSqlHana = function toSqlHana(context, parameter) {
let p = [];
let ret = '(' + this.value.toSqlHana(context, p) + ')';
parameter.push.apply(parameter, p);
return ret;
};
Parenthesis.prototype.setAlias = function (alias) {
if (this.value.setAlias) {
this.value.setAlias(alias);
}
};
Parenthesis.prototype.applyConverter = createApplyConverter('value');
Parenthesis.prototype.getChildren = function () {
return [this.value];
};
/** Member
* @class Member
* Tested via test_filter_parser.js
*/
function Member(path, property) {
this.kind = exports.NODE_MEMBER;
this.path = path;
this.property = property;
}
Member.prototype.toSqlHana = function toSqlHana() {
throw new NotSupported('Member access not supported');
};
Member.prototype.getChildren = function () {
return [];
};
/** Property
* @class Property
* Tested via test_filter_parser.js
*/
function Property(property, table, dbType) {
this.kind = exports.NODE_PROPERTY;
this.property = property;
this.table = table;
this.dbType = dbType;
}
Property.prototype.toSqlHana = function toSqlHana(context) {
let ret = '';
let table = (this.table ? this.table : context.table);
if (table) {
ret += '"' + table + '"' + ".";
}
ret += '"' + this.property + '"';
return ret;
};
Property.prototype.setAlias = function (alias) {
this.table = alias;
};
Property.prototype.getChildren = function () {
return [];
};
/** SortOrder
* @class SortOrder
* Tested via test_oderby_parser.js
*/
function SortOrder(expression, ascending) {
this.kind = exports.NODE_SORTORDER;
this.expression = expression;
this.ascending = ascending;
}
SortOrder.prototype.setPropertyName = function(propertyName) {
this.propertyName = propertyName;
};
SortOrder.prototype.getPropertyName = function() {
return this.propertyName;
};
SortOrder.prototype.setAlias = function (alias) {
this.expression.setAlias(alias);
};
SortOrder.prototype.toSqlHana = function toSqlHana(context, parameter) {
let sql = '';
let p = [];
sql += this.expression.toSqlHana(context, p);
parameter.push.apply(parameter, p);
if (this.ascending === false) {
sql += ' DESC';
}
sql += '';
return sql;
};
SortOrder.prototype.applyConverter = createApplyConverter('expression');
SortOrder.prototype.getChildren = function () {
return [this.expression];
};
/** SortOrder
* @class SortOrder
* Tested via test_oderby_parser.js
*/
function OrderBy() {
this.kind = exports.NODE_ORDERBY;
this.orders = [];
}
OrderBy.prototype.addSortOrder = function (sortOrders) {
this.orders.push(sortOrders);
};
OrderBy.prototype.applyConverter = function (convert) {
this.orders.forEach(function (child) {
child.applyConverter(convert);
});
};
OrderBy.prototype.setAlias = function (alias) {
for (let i = 0; i < this.orders.length; i++) {
this.orders[i].setAlias(alias);
}
};
OrderBy.prototype.toSqlHana = function toSqlHana(context, parameter) {
let sql = '';
let clonedContext = cloneContextWithNoAlias(context);
clonedContext.inOrderBy = true;
for (let i = 0; i < this.orders.length; i++) {
sql += (i === 0 ? '' : ', ');
sql += this.orders[i].toSqlHana(clonedContext, parameter);
}
return sql;
};
OrderBy.prototype.getChildren = function () {
return this.orders;
};
/**
* Creates an applyConverter function for the given property names.
*
* @returns {Function} applyConvert to be attached to an object, who properties shall be converted
*/
function createApplyConverter() {
let childPropertyNames = [].slice.call(arguments);
/**
* The created function takes a converter function as argument, which
* takes a property to be converted. The value returned by this function
* will then replace the property on the object in which context applyConverter
* has been called.
*
* Afterwards, if the property also has an applyConverter, it is called with the
* same converter function.
* @param function to be called with the configured properties
*/
return function applyConverter(convert) {
childPropertyNames.forEach(function (property) {
this[property] = convert(this[property]);
if (this[property].applyConverter) {
this[property].applyConverter(convert);
}
}.bind(this));
};
}
function cloneContextWithNoAlias(context) {
let clonedContext;
if (context.clone) {
clonedContext = context.clone();
} else {
clonedContext = {};
}
clonedContext.noAlias = true;
return clonedContext;
}