1+ /* eslint-disable sonarjs/no-identical-functions */
12import type {
23 TableDomain ,
34 IFunctionCallInfo ,
@@ -59,6 +60,14 @@ export class FormulaSupportGeneratedColumnValidator {
5960 return false ;
6061 }
6162
63+ if ( this . hasLogicalNonBooleanArgs ( tree ) ) {
64+ return false ;
65+ }
66+
67+ if ( this . containsLogicalFunctions ( tree ) ) {
68+ return false ;
69+ }
70+
6271 // Extract all function calls from the AST
6372 const collector = new FunctionCallCollectorVisitor ( ) ;
6473 const functionCalls = collector . visit ( tree ) ;
@@ -137,10 +146,16 @@ export class FormulaSupportGeneratedColumnValidator {
137146
138147 // If it's a formula field, recursively check its dependencies
139148 if ( field . type === FieldType . Formula ) {
149+ const formulaField = field as FormulaFieldCore ;
150+
151+ if ( ! formulaField . getIsPersistedAsGeneratedColumn ( ) ) {
152+ return false ;
153+ }
154+
140155 visitedFields . add ( fieldId ) ;
141156
142157 try {
143- const expression = ( field as FormulaFieldCore ) . getExpression ( ) ;
158+ const expression = formulaField . getExpression ( ) ;
144159 if ( expression ) {
145160 const tree = parseFormula ( expression ) ;
146161 return this . validateFieldReferences ( tree , visitedFields ) ;
@@ -554,12 +569,13 @@ export class FormulaSupportGeneratedColumnValidator {
554569
555570 // eslint-disable-next-line sonarjs/no-identical-functions
556571 visitChildren ( node : RuleNode ) : boolean {
557- const n = node . childCount ;
558- for ( let i = 0 ; i < n ; i ++ ) {
559- const child = node . getChild ( i ) ;
572+ let index = 0 ;
573+ while ( index < node . childCount ) {
574+ const child = node . getChild ( index ) ;
560575 if ( child && child . accept ( this ) ) {
561576 return true ;
562577 }
578+ index ++ ;
563579 }
564580 return false ;
565581 }
@@ -582,6 +598,89 @@ export class FormulaSupportGeneratedColumnValidator {
582598 return tree . accept ( new DatetimeConcatDetector ( ) ) ?? false ;
583599 }
584600
601+ private hasLogicalNonBooleanArgs ( tree : ExprContext ) : boolean {
602+ // eslint-disable-next-line @typescript-eslint/no-this-alias
603+ const self = this ;
604+ class LogicalArgumentDetector extends AbstractParseTreeVisitor < boolean > {
605+ protected defaultResult ( ) : boolean {
606+ return false ;
607+ }
608+
609+ visitChildren ( node : RuleNode ) : boolean {
610+ const n = node . childCount ;
611+ for ( let i = 0 ; i < n ; i ++ ) {
612+ const child = node . getChild ( i ) ;
613+ if ( child && child . accept ( this ) ) {
614+ return true ;
615+ }
616+ }
617+ return false ;
618+ }
619+
620+ visitFunctionCall ( ctx : FunctionCallContext ) : boolean {
621+ const rawName = ctx . func_name ( ) . text . toUpperCase ( ) ;
622+ const fnName = normalizeFunctionNameAlias ( rawName ) as FunctionName ;
623+ const isLogical =
624+ fnName === FunctionName . And ||
625+ fnName === FunctionName . Or ||
626+ fnName === FunctionName . Not ||
627+ fnName === FunctionName . Xor ;
628+
629+ if ( isLogical ) {
630+ const exprs = ctx . expr ( ) ;
631+ for ( const exprCtx of exprs ) {
632+ const argType = self . inferBasicType ( exprCtx ) ;
633+ if ( argType === 'string' || argType === 'number' || argType === 'datetime' ) {
634+ return true ;
635+ }
636+ }
637+ }
638+
639+ return this . visitChildren ( ctx ) ;
640+ }
641+ }
642+
643+ return tree . accept ( new LogicalArgumentDetector ( ) ) ?? false ;
644+ }
645+
646+ private containsLogicalFunctions ( tree : ExprContext ) : boolean {
647+ class LogicalFunctionDetector extends AbstractParseTreeVisitor < boolean > {
648+ protected defaultResult ( ) : boolean {
649+ return false ;
650+ }
651+
652+ visitChildren ( node : RuleNode ) : boolean {
653+ let index = 0 ;
654+ while ( index < node . childCount ) {
655+ const child = node . getChild ( index ) ;
656+ if ( child && child . accept ( this ) ) {
657+ return true ;
658+ }
659+ index ++ ;
660+ }
661+ return false ;
662+ }
663+
664+ visitFunctionCall ( ctx : FunctionCallContext ) : boolean {
665+ const rawName = ctx . func_name ( ) . text . toUpperCase ( ) ;
666+ const fnName = normalizeFunctionNameAlias ( rawName ) as FunctionName ;
667+ const isLogical =
668+ fnName === FunctionName . And ||
669+ fnName === FunctionName . Or ||
670+ fnName === FunctionName . Not ||
671+ fnName === FunctionName . Xor ;
672+
673+ if ( isLogical ) {
674+ return true ;
675+ }
676+
677+ return this . visitChildren ( ctx ) ;
678+ }
679+ }
680+
681+ return tree . accept ( new LogicalFunctionDetector ( ) ) ?? false ;
682+ }
683+
585684 // eslint-disable-next-line sonarjs/cognitive-complexity
586685 private inferBasicType (
587686 ctx : ExprContext
0 commit comments