@@ -22,7 +22,6 @@ import {
2222 localTimestampToUtc ,
2323 timeSeries as timeSeriesBase ,
2424 timeSeriesFromCustomInterval ,
25- parseSqlInterval ,
2625 findMinGranularityDimension
2726} from '@cubejs-backend/shared' ;
2827
@@ -436,81 +435,32 @@ export class BaseQuery {
436435 */
437436 get allJoinHints ( ) {
438437 if ( ! this . collectedJoinHints ) {
439- const [ rootOfJoin , ... allMembersJoinHints ] = this . collectJoinHintsFromMembers ( this . allMembersConcat ( false ) ) ;
438+ const allMembersJoinHints = this . collectJoinHintsFromMembers ( this . allMembersConcat ( false ) ) ;
440439 const customSubQueryJoinHints = this . collectJoinHintsFromMembers ( this . joinMembersFromCustomSubQuery ( ) ) ;
441- let joinMembersJoinHints = this . collectJoinHintsFromMembers ( this . joinMembersFromJoin ( this . join ) ) ;
442-
443- // One cube may join the other cube via transitive joined cubes,
444- // members from which are referenced in the join `on` clauses.
445- // We need to collect such join hints and push them upfront of the joining one
446- // but only if they don't exist yet. Cause in other case we might affect what
447- // join path will be constructed in join graph.
448- // It is important to use queryLevelJoinHints during the calculation if it is set.
449-
450- const constructJH = ( ) => {
451- const filteredJoinMembersJoinHints = joinMembersJoinHints . filter ( m => ! allMembersJoinHints . includes ( m ) ) ;
452- return [
453- ...this . queryLevelJoinHints ,
454- ...( rootOfJoin ? [ rootOfJoin ] : [ ] ) ,
455- ...filteredJoinMembersJoinHints ,
456- ...allMembersJoinHints ,
457- ...customSubQueryJoinHints ,
458- ] ;
459- } ;
460-
461- let prevJoins = this . join ;
462- let prevJoinMembersJoinHints = joinMembersJoinHints ;
463- let newJoin = this . joinGraph . buildJoin ( constructJH ( ) ) ;
464-
465- const isOrderPreserved = ( base , updated ) => {
466- const common = base . filter ( value => updated . includes ( value ) ) ;
467- const bFiltered = updated . filter ( value => common . includes ( value ) ) ;
468-
469- return common . every ( ( x , i ) => x === bFiltered [ i ] ) ;
470- } ;
471-
472- const isJoinTreesEqual = ( a , b ) => {
473- if ( ! a || ! b || a . root !== b . root || a . joins . length !== b . joins . length ) {
474- return false ;
475- }
476-
477- // We don't care about the order of joins on the same level, so
478- // we can compare them as sets.
479- const aJoinsSet = new Set ( a . joins . map ( j => `${ j . originalFrom } ->${ j . originalTo } ` ) ) ;
480- const bJoinsSet = new Set ( b . joins . map ( j => `${ j . originalFrom } ->${ j . originalTo } ` ) ) ;
481-
482- if ( aJoinsSet . size !== bJoinsSet . size ) {
483- return false ;
484- }
485-
486- for ( const val of aJoinsSet ) {
487- if ( ! bJoinsSet . has ( val ) ) {
488- return false ;
489- }
490- }
491-
492- return true ;
493- } ;
440+ const allJoinHints = [
441+ ...this . queryLevelJoinHints ,
442+ ...allMembersJoinHints ,
443+ ...customSubQueryJoinHints ,
444+ ] ;
494445
495- // Safeguard against infinite loop in case of cyclic joins somehow managed to slip through
496- let cnt = 0 ;
446+ const tempJoin = this . joinGraph . buildJoin ( allJoinHints ) ;
497447
498- while ( newJoin ?. joins . length > 0 && ! isJoinTreesEqual ( prevJoins , newJoin ) && cnt < 10000 ) {
499- prevJoins = newJoin ;
500- joinMembersJoinHints = this . collectJoinHintsFromMembers ( this . joinMembersFromJoin ( newJoin ) ) ;
501- if ( ! isOrderPreserved ( prevJoinMembersJoinHints , joinMembersJoinHints ) ) {
502- throw new UserError ( `Can not construct joins for the query, potential loop detected: ${ prevJoinMembersJoinHints . join ( '->' ) } vs ${ joinMembersJoinHints . join ( '->' ) } ` ) ;
503- }
504- newJoin = this . joinGraph . buildJoin ( constructJH ( ) ) ;
505- prevJoinMembersJoinHints = joinMembersJoinHints ;
506- cnt ++ ;
448+ if ( ! tempJoin ) {
449+ this . collectedJoinHints = allJoinHints ;
450+ return allJoinHints ;
507451 }
508452
509- if ( cnt >= 10000 ) {
510- throw new UserError ( 'Can not construct joins for the query, potential loop detected' ) ;
511- }
453+ const joinMembersJoinHints = this . collectJoinHintsFromMembers ( this . joinMembersFromJoin ( tempJoin ) ) ;
454+ const allJoinHintsFlatten = new Set ( allJoinHints . flat ( ) ) ;
455+ const newCollectedHints = joinMembersJoinHints . filter ( j => ! allJoinHintsFlatten . has ( j ) ) ;
512456
513- this . collectedJoinHints = R . uniq ( constructJH ( ) ) ;
457+ this . collectedJoinHints = [
458+ ...this . queryLevelJoinHints ,
459+ tempJoin . root ,
460+ ...newCollectedHints ,
461+ ...allMembersJoinHints ,
462+ ...customSubQueryJoinHints ,
463+ ] ;
514464 }
515465 return this . collectedJoinHints ;
516466 }
0 commit comments