UNPKG

@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
'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; }