diff --git a/packages/nocodb-sdk/src/lib/formulaHelpers.ts b/packages/nocodb-sdk/src/lib/formulaHelpers.ts index db6a72668b..b84499821a 100644 --- a/packages/nocodb-sdk/src/lib/formulaHelpers.ts +++ b/packages/nocodb-sdk/src/lib/formulaHelpers.ts @@ -1496,16 +1496,38 @@ export function validateFormulaAndExtractTreeWithType({ } else { res.dataType = FormulaDataTypes.STRING; } - } else if ( - parsedTree.type === JSEPNode.BINARY_EXP || - parsedTree.type === JSEPNode.UNARY_EXP - ) { + } else if (parsedTree.type === JSEPNode.UNARY_EXP) { + throw new FormulaError( + FormulaErrorType.NOT_SUPPORTED, + {}, + 'Unary expression is not supported' + ); + } else if (parsedTree.type === JSEPNode.BINARY_EXP) { res.left = validateAndExtract(parsedTree.left); res.right = validateAndExtract(parsedTree.right); if (['==', '<', '>', '<=', '>=', '!='].includes(parsedTree.operator)) { res.dataType = FormulaDataTypes.COND_EXP; - } else res.dataType = FormulaDataTypes.NUMERIC; + } + else if (parsedTree.operator === '+') { + res.dataType = FormulaDataTypes.NUMERIC; + // if any side is string/date/other type, then the result will be concatenated string + // e.g. 1 + '2' = '12' + [res.left, res.right].some( + (r) => + ![ + FormulaDataTypes.NUMERIC, + FormulaDataTypes.BOOLEAN, + FormulaDataTypes.NULL, + FormulaDataTypes.UNKNOWN, + ].includes(r.dataType) + ) + ) { + res.dataType = FormulaDataTypes.STRING; + } + } else { + res.dataType = FormulaDataTypes.NUMERIC; + } } return res; diff --git a/packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts b/packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts index 38c2997117..e6764da560 100644 --- a/packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts +++ b/packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts @@ -833,6 +833,22 @@ async function _formulaQueryBuilder( ); } + // if operator is + and expected return type is string, convert to concat + if (pt.operator === '+' && pt.dataType === FormulaDataTypes.STRING) { + return fn( + { + type: 'CallExpression', + arguments: [pt.left, pt.right], + callee: { + type: 'Identifier', + name: 'CONCAT', + }, + }, + alias, + prevBinaryOp, + ); + } + if (pt.operator === '==') { pt.operator = '='; // if left/right is of different type, convert to string and compare