From 60f50fdb4e4cfc89f38ae881aad628305b85021e Mon Sep 17 00:00:00 2001 From: Sebastian Listl <s.listl@adito.de> Date: Wed, 28 Oct 2020 14:55:50 +0100 Subject: [PATCH] Sql_lib case when --- process/Sql_lib/process.js | 217 +++++++++++++++++++++++++++++++++---- 1 file changed, 193 insertions(+), 24 deletions(-) diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js index 3b2f720defa..35b87999459 100644 --- a/process/Sql_lib/process.js +++ b/process/Sql_lib/process.js @@ -801,6 +801,26 @@ function SqlBuilder (pAlias) this._where = {}; this._initWhere(); + + SqlBuilder.defineCanBuildSql(this); +} + +/** + * @return {Symbol} + */ +SqlBuilder.getCanBuildSqlSymbol = function () +{ + return Symbol["for"]("canBuildSql"); +} + +SqlBuilder.defineCanBuildSql = function (pObject) +{ + pObject[SqlBuilder.getCanBuildSqlSymbol()] = true; +} + +SqlBuilder.checkCanBuildSql = function (pObject) +{ + return pObject[SqlBuilder.getCanBuildSqlSymbol()]; } /** @@ -1097,11 +1117,11 @@ SqlBuilder.prototype.join = function(pTable, pCondition, pTableAlias, pPrefix, p if (pCondition) { if (pCondition instanceof SqlBuilder) - pCondition = [pCondition._where._sqlStorage, pCondition._where.preparedValues] + pCondition = [pCondition._where.sqlStorage, pCondition._where.preparedValues] var conditionPart = SqlBuilder._getStatement(pCondition); - joinPart._sqlStorage += " " + conditionPart._sqlStorage; + joinPart.sqlStorage += " " + conditionPart.sqlStorage; joinPart.preparedValues = joinPart.preparedValues.concat(conditionPart.preparedValues); } @@ -1200,7 +1220,7 @@ SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldTy copiedCondition._where.preparedValues = pFieldOrCond.preparedValues; copiedCondition._where._lastWasOr = pFieldOrCond._lastWasOr; - copiedCondition._where._sqlStorage = pFieldOrCond._sqlStorage; + copiedCondition._where.sqlStorage = pFieldOrCond.sqlStorage; pFieldOrCond = copiedCondition; @@ -1351,7 +1371,7 @@ SqlBuilder.prototype._whereCondition = function(pCondition, pMandatory, pAddPrep if (sql instanceof SqlBuilder) { // add only brackets if needed - var sqlString = sql._where._sqlStorage; + var sqlString = sql._where.sqlStorage; var condString = sqlString; @@ -1509,7 +1529,7 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon var subSqlPrepared = pFieldOrCond.build(); - tmpCond._where._sqlStorage = SqlUtils.replaceConditionTemplate(tmpCond._where._sqlStorage, 'SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN', "( " + subSqlPrepared[0] + " )"); + tmpCond._where.sqlStorage = SqlUtils.replaceConditionTemplate(tmpCond._where.sqlStorage, 'SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN', "( " + subSqlPrepared[0] + " )"); tmpCond._where.preparedValues = subSqlPrepared[1].concat(tmpCond._where.preparedValues) this._whereCondition(tmpCond, pMandatory, pAddPreparedConditionCallback, true) @@ -1629,9 +1649,9 @@ SqlBuilder.prototype._and = function(pFieldOrCond, pValue, pMandatory, pConditio if (pPreparedCondition.length == 2 && typeof pPreparedCondition[0] == "string" && pPreparedCondition[0] != "" && Array.isArray(pPreparedCondition[1])) { if (that.hasCondition()) - that._where._sqlStorage += " and "; + that._where.sqlStorage += " and "; - that._where._sqlStorage += pPreparedCondition[0]; + that._where.sqlStorage += pPreparedCondition[0]; that._where.preparedValues = that._where.preparedValues.concat(pPreparedCondition[1]); } }); @@ -1657,7 +1677,7 @@ SqlBuilder.prototype._or = function(pFieldOrCond, pValue, pMandatory, pCondition { if (that._where._previouslyOnlyOr) { - that._where._sqlStorage = that._where._sqlStorage + " or " + pPreparedCondition[0]; + that._where.sqlStorage = that._where.sqlStorage + " or " + pPreparedCondition[0]; that._where._lastWasOr = true; } else if (that.hasCondition()) @@ -1668,9 +1688,9 @@ SqlBuilder.prototype._or = function(pFieldOrCond, pValue, pMandatory, pCondition cond = "(" + cond + ")"; if (that._where._lastWasOr) - that._where._sqlStorage = that._where._sqlStorage + " or " + cond; + that._where.sqlStorage = that._where.sqlStorage + " or " + cond; else - that._where._sqlStorage = "(" + that._where._sqlStorage + ") or " + cond; + that._where.sqlStorage = "(" + that._where.sqlStorage + ") or " + cond; that._where._lastWasOr = true; } @@ -1679,7 +1699,7 @@ SqlBuilder.prototype._or = function(pFieldOrCond, pValue, pMandatory, pCondition if (!that.hasCondition()) that._where._previouslyOnlyOr = true; - that._where._sqlStorage = pPreparedCondition[0]; + that._where.sqlStorage = pPreparedCondition[0]; } that._where.preparedValues = that._where.preparedValues.concat(pPreparedCondition[1]); } @@ -2170,7 +2190,7 @@ SqlBuilder.prototype.having = function(pCondition) * @return {Boolean} true if conditions have been added, false when not */ SqlBuilder.prototype.hasCondition = function() { - if (this._where._sqlStorage) + if (this._where.sqlStorage) return true; return false; } @@ -2217,7 +2237,7 @@ SqlBuilder.prototype.clearWhere = function() SqlBuilder.prototype._initWhere = function () { //TODO: maybe put conditions in an object/array for better internal object structure - this._where._sqlStorage = ""; + this._where.sqlStorage = ""; this._where.preparedValues = []; this._where._lastWasOr = false; // save, if the last condition was an OR. For better bracket-placement this._where._previouslyOnlyOr = false; // also for better bracket-placement @@ -2329,24 +2349,22 @@ SqlBuilder._getStatement = function (pElement, pPrefix, pPostfix, pAutoJoin, pUs return { preparedValues: preparedValues, - _sqlStorage: pElement.toString() + sqlStorage: pElement.toString() }; function _getElement (element) { var isSubQuery = false; var subselectAlias = ""; - if (element instanceof SqlBuilder) + if (SqlBuilder.checkCanBuildSql(element)) { - if (element.isFullSelect()) + if (element instanceof SqlBuilder && element.isFullSelect()) { isSubQuery = true; if (pUseSubselectAlias && element._subselectAlias) subselectAlias = " " + element._subselectAlias; } - - element = element.build(); } preparedValues = preparedValues.concat(element[1]); @@ -2363,7 +2381,7 @@ SqlBuilder._getStatement = function (pElement, pPrefix, pPostfix, pAutoJoin, pUs */ SqlBuilder.prototype.buildCondition = function() { - return [this._where._sqlStorage, this._where.preparedValues]; + return [this._where.sqlStorage, this._where.preparedValues]; } /** @@ -2378,17 +2396,17 @@ SqlBuilder.prototype.build = function(pDefaultConditionIfNone) if (this.isFullSelect()) { - if (this._where._sqlStorage) + if (this._where.sqlStorage) wherePrefix = "where "; } - var whereSql = this._where._sqlStorage; + var whereSql = this._where.sqlStorage; if (!this.hasCondition() && pDefaultConditionIfNone) whereSql = wherePrefix + pDefaultConditionIfNone; var whereObj = { - _sqlStorage : wherePrefix + whereSql, + sqlStorage : wherePrefix + whereSql, preparedValues : this._where.preparedValues } @@ -2409,9 +2427,9 @@ SqlBuilder.prototype.build = function(pDefaultConditionIfNone) let part = allParts[i]; if (part) { - if (sqlStr && part._sqlStorage) + if (sqlStr && part.sqlStorage) sqlStr += " "; - sqlStr += part._sqlStorage; + sqlStr += part.sqlStorage; if (part.preparedValues.length) preparedVals = preparedVals.concat(part.preparedValues); } @@ -3057,6 +3075,157 @@ SqlBuilder.prototype.translate = function(pAlias) return SqlUtils.translateStatementWithQuotes(this.build(), pAlias); } +/** + * Creates an object for building a case-when statement. + * + * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/> + * else it is used as Field. <br/> + * Please see .where() for more information and examples. + * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/> + * Basically it can be nearly everything you need.<br/> + * Please see .where() for more information and examples. + * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/> + * # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/> + * ? will be replaced by pValue<br/> + * <strong>IMPORTANT: the # has to be before the ?</strong><br/> + * Please see .where() for more information and examples. + * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/> + * In most cases you don't need this.<br/> + * Please see .where() for more information and examples. + * + * @return {SqlBuilder._CaseWhen} + */ +SqlBuilder.caseWhen = function (pFieldOrCond, pValue, pCondition, pFieldType) +{ + return new SqlBuilder._CaseStatement().when(pFieldOrCond, pValue, pCondition, pFieldType); +} + +/** + * Represents a case-when statement + */ +SqlBuilder._CaseStatement = function () +{ + this._whenCondition = null; + this._whenThens = []; + this._elseValue = null; + this._afterWhenMask = new SqlBuilder._CaseWhen(this); + SqlBuilder.defineCanBuildSql(this); +} + +/** + * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/> + * else it is used as Field. <br/> + * Please see .where() for more information and examples. + * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/> + * Basically it can be nearly everything you need.<br/> + * Please see .where() for more information and examples. + * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/> + * # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/> + * ? will be replaced by pValue<br/> + * <strong>IMPORTANT: the # has to be before the ?</strong><br/> + * Please see .where() for more information and examples. + * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/> + * In most cases you don't need this.<br/> + * Please see .where() for more information and examples. + * + * @return {SqlBuilder._CaseWhen} + */ +SqlBuilder._CaseStatement.prototype.when = function (pFieldOrCond, pValue, pCondition, pFieldType) +{ + this._whenCondition = newWhere(pFieldOrCond, pValue, pCondition, pFieldType); + return this._afterWhenMask; +} + +/** + * Sets the expression used for the else-part + * + * @param {String|SqlBuilder} pValue else-value + * @return {SqlBuilder._CaseStatement} + */ +SqlBuilder._CaseStatement.prototype.elseValue = function (pValue) +{ + this._elseValue = pValue; + return this; +} + +/** + * Sets the value used for the else-part, but wraps the value in '' + * + * @param {String} pValue else-value + * @return {SqlBuilder._CaseStatement} + */ +SqlBuilder._CaseStatement.prototype.elseString = function (pValue) +{ + return this.elseValue("'" + pValue + "'"); +} + +/** + * @return {String} the case-when expression + */ +SqlBuilder._CaseStatement.prototype.toString = function (pAlias) +{ + return db.translateStatement(this.build(), pAlias || db.getCurrentAlias()); +} + +SqlBuilder._CaseStatement.prototype.build = function () +{ + var caseStatement = ["case"]; + var preparedValues = []; + this._whenThens.forEach(function (whenThen) + { + var when = SqlBuilder._getStatement(whenThen.condition, "when"); + var then = SqlBuilder._getStatement(whenThen.thenValue, "then"); + caseStatement.push(when.sqlStorage); + caseStatement.push(then.sqlStorage); + preparedValues = preparedValues.concat(when.preparedValues, then.preparedValues); + }); + if (this._elseValue) + { + let elseStatement = SqlBuilder._getStatement(this._elseValue, "else"); + caseStatement.push(elseStatement.sqlStorage); + preparedValues = preparedValues.concat(elseStatement.preparedValues); + } + caseStatement.push("end"); + + return [ + caseStatement.join(" "), + preparedValues + ]; +} + +/** + * Object providing the then-methods for the case-when expression. It can be only be accessed after calling .when to ensure a 'then' + * can only be added after a 'when'. + */ +SqlBuilder._CaseWhen = function (pCaseStatement) +{ + this._caseStatement = pCaseStatement; +} + +/** + * Sets the expression for the then + * + * @param {String|SqlBuilder} pValue then-value + * @return {SqlBuilder._CaseStatement} + */ +SqlBuilder._CaseWhen.prototype.then = function (pValue) +{ + var condition = this._caseStatement._whenCondition; + this._caseStatement._whenCondition = null; + this._caseStatement._whenThens.push({condition: condition, thenValue: pValue}); + return this._caseStatement; +} + +/** + * Sets the value for the then, but wraps the value in '' + * + * @param {String} pValue then-value + * @return {SqlBuilder._CaseStatement} + */ +SqlBuilder._CaseWhen.prototype.thenString = function (pValue) +{ + return this.then("'" + pValue + "'"); +} /** *provides functions for masking sql functions -- GitLab