@@ -425,35 +425,26 @@ class QuestDBParser extends CstParser {
425425 } )
426426 } )
427427
428- // Lazy-initialized BACKTRACK for WITH ... INSERT disambiguation
429- private _insertBacktrack : ( ( ) => boolean ) | null = null
430- private getInsertBacktrack ( ) : boolean {
431- if ( ! this . _insertBacktrack ) {
432- this . _insertBacktrack = this . BACKTRACK ( this . insertStatement )
433- }
434- return this . _insertBacktrack ( )
435- }
436-
437428 public statement = this . RULE ( "statement" , ( ) => {
438429 // Record token position so findReSyncTokenType can distinguish
439430 // "statement's OR failed" (no tokens consumed) from "error cascaded
440431 // from inside a matched subrule" (tokens consumed).
441432 this . _statementStartIdx = ( this as unknown as CstParserInternals ) . currIdx
442433 this . OR ( [
443434 {
444- // Use simple token check for INSERT (common case) so error recovery
445- // works for incomplete statements. Only fall back to BACKTRACK for
446- // WITH ... INSERT (ambiguous with selectStatement's WITH clause).
447- GATE : ( ) => {
448- const la1 = this . LA ( 1 ) . tokenType
449- if ( la1 === Insert ) return true
450- if ( la1 === With ) return this . getInsertBacktrack ( )
451- return false
452- } ,
435+ // WITH-prefixed statements (CTE): parse WITH clause once, then
436+ // dispatch to the correct body via keyword lookahead. This avoids
437+ // BACKTRACK gates that re-parse the entire CTE prefix for each
438+ // alternative.
439+ GATE : ( ) => this . LA ( 1 ) . tokenType === With ,
440+ ALT : ( ) => this . SUBRULE ( this . withStatement ) ,
441+ } ,
442+ {
443+ GATE : ( ) => this . LA ( 1 ) . tokenType === Insert ,
453444 ALT : ( ) => this . SUBRULE ( this . insertStatement ) ,
454445 } ,
455446 {
456- GATE : this . BACKTRACK ( this . updateStatement ) ,
447+ GATE : ( ) => this . LA ( 1 ) . tokenType === Update ,
457448 ALT : ( ) => this . SUBRULE ( this . updateStatement ) ,
458449 } ,
459450 { ALT : ( ) => this . SUBRULE ( this . selectStatement ) } ,
@@ -501,6 +492,34 @@ class QuestDBParser extends CstParser {
501492 ] )
502493 } )
503494
495+ // ==========================================================================
496+ // WITH Statement (CTE wrapper)
497+ // ==========================================================================
498+ //
499+ // Parses the WITH clause once, then dispatches to the correct body
500+ // (INSERT, UPDATE, or SELECT) via simple keyword lookahead.
501+ // This avoids the old BACKTRACK gates which re-parsed the entire CTE
502+ // prefix for each alternative, causing O(n*alternatives) work.
503+
504+ private withStatement = this . RULE ( "withStatement" , ( ) => {
505+ this . SUBRULE ( this . withClause )
506+ this . OR ( [
507+ {
508+ GATE : ( ) => this . LA ( 1 ) . tokenType === Insert ,
509+ ALT : ( ) => this . SUBRULE ( this . insertStatement ) ,
510+ } ,
511+ {
512+ GATE : ( ) => this . LA ( 1 ) . tokenType === Update ,
513+ ALT : ( ) => this . SUBRULE ( this . updateStatement ) ,
514+ } ,
515+ {
516+ // SELECT: delegate to selectStatement (its optional declareClause/
517+ // withClause simply won't match since WITH was already consumed)
518+ ALT : ( ) => this . SUBRULE ( this . selectStatement ) ,
519+ } ,
520+ ] )
521+ } )
522+
504523 // ==========================================================================
505524 // SELECT Statement
506525 // ==========================================================================
@@ -1045,7 +1064,6 @@ class QuestDBParser extends CstParser {
10451064 // ==========================================================================
10461065
10471066 private insertStatement = this . RULE ( "insertStatement" , ( ) => {
1048- this . OPTION ( ( ) => this . SUBRULE ( this . withClause ) )
10491067 this . CONSUME ( Insert )
10501068 this . OPTION1 ( ( ) => {
10511069 this . OR ( [
@@ -1098,7 +1116,6 @@ class QuestDBParser extends CstParser {
10981116 // ==========================================================================
10991117
11001118 private updateStatement = this . RULE ( "updateStatement" , ( ) => {
1101- this . OPTION4 ( ( ) => this . SUBRULE ( this . withClause ) )
11021119 this . CONSUME ( Update )
11031120 this . SUBRULE ( this . qualifiedName )
11041121 // Optional alias
0 commit comments