@@ -28,6 +28,25 @@ func (p *Parser) isMariaDB() bool {
2828 return p .dialect == string (keywords .DialectMariaDB )
2929}
3030
31+ // isMariaDBClauseStart returns true when the current token is the start of a
32+ // MariaDB hierarchical-query clause (CONNECT BY or START WITH) rather than a
33+ // table alias. Used to guard alias parsing in FROM and JOIN table references.
34+ func (p * Parser ) isMariaDBClauseStart () bool {
35+ if ! p .isMariaDB () {
36+ return false
37+ }
38+ val := strings .ToUpper (p .currentToken .Token .Value )
39+ if val == "CONNECT" {
40+ next := p .peekToken ()
41+ return strings .EqualFold (next .Token .Value , "BY" )
42+ }
43+ if val == "START" {
44+ next := p .peekToken ()
45+ return strings .EqualFold (next .Token .Value , "WITH" )
46+ }
47+ return false
48+ }
49+
3150// parseCreateSequenceStatement parses:
3251//
3352// CREATE [OR REPLACE] SEQUENCE [IF NOT EXISTS] name [options...]
@@ -73,7 +92,9 @@ func (p *Parser) parseDropSequenceStatement() (*ast.DropSequenceStatement, error
7392 if strings .EqualFold (p .currentToken .Token .Value , "IF" ) {
7493 p .advance ()
7594 if strings .EqualFold (p .currentToken .Token .Value , "NOT" ) {
76- // IF NOT EXISTS — treated as "no error if absent" (same semantics as IF EXISTS)
95+ // IF NOT EXISTS is a non-standard permissive extension (MariaDB only supports
96+ // IF EXISTS natively). We accept it and reuse the IfExists flag since both
97+ // forms mean "suppress the error if the sequence is absent".
7798 p .advance ()
7899 if ! strings .EqualFold (p .currentToken .Token .Value , "EXISTS" ) {
79100 return nil , p .expectedError ("EXISTS" )
@@ -181,6 +202,7 @@ func (p *Parser) parseSequenceOptions() (ast.SequenceOptions, error) {
181202 opts .NoCycle = true
182203 case "CACHE" :
183204 opts .Cache = nil
205+ opts .NoCache = true
184206 default :
185207 return opts , fmt .Errorf ("unexpected token after NO in SEQUENCE options: %s" , sub )
186208 }
@@ -239,9 +261,11 @@ func (p *Parser) parseForSystemTimeClause() (*ast.ForSystemTimeClause, error) {
239261 if ! strings .EqualFold (p .currentToken .Token .Value , "SYSTEM_TIME" ) {
240262 return nil , fmt .Errorf ("expected SYSTEM_TIME after FOR, got %q" , p .currentToken .Token .Value )
241263 }
264+ sysTimePos := p .currentLocation () // position of SYSTEM_TIME token
242265 p .advance ()
243266
244267 clause := & ast.ForSystemTimeClause {}
268+ clause .Pos = sysTimePos
245269 word := strings .ToUpper (p .currentToken .Token .Value )
246270
247271 switch word {
@@ -327,12 +351,15 @@ func (p *Parser) parseTemporalPointExpression() (ast.Expression, error) {
327351// parseConnectByCondition parses the condition expression for CONNECT BY.
328352// It handles the PRIOR prefix operator in either position:
329353//
330- // CONNECT BY PRIOR id = parent_id (PRIOR on left)
331- // CONNECT BY id = PRIOR parent_id (PRIOR on right)
354+ // CONNECT BY PRIOR id = parent_id (PRIOR on left)
355+ // CONNECT BY id = PRIOR parent_id (PRIOR on right)
356+ // CONNECT BY PRIOR id = parent_id AND active = 1 (complex with AND/OR)
332357//
333358// PRIOR references the value from the parent row in the hierarchy.
334359// It is modeled as UnaryExpression{Operator: ast.Prior, Expr: <column>}.
335360func (p * Parser ) parseConnectByCondition () (ast.Expression , error ) {
361+ var base ast.Expression
362+
336363 // Case 1: PRIOR col op col
337364 if strings .EqualFold (p .currentToken .Token .Value , "PRIOR" ) {
338365 p .advance ()
@@ -351,38 +378,57 @@ func (p *Parser) parseConnectByCondition() (ast.Expression, error) {
351378 if err != nil {
352379 return nil , err
353380 }
354- return & ast.BinaryExpression {Left : priorExpr , Operator : op , Right : right }, nil
381+ base = & ast.BinaryExpression {Left : priorExpr , Operator : op , Right : right }
382+ } else {
383+ base = priorExpr
355384 }
356- return priorExpr , nil
357- }
358-
359- // Case 2: col op PRIOR col (PRIOR on the right-hand side)
360- left , err := p .parsePrimaryExpression ()
361- if err != nil {
362- return nil , err
363- }
364- if p .isType (models .TokenTypeEq ) || p .isType (models .TokenTypeNeq ) ||
365- p .isType (models .TokenTypeLt ) || p .isType (models .TokenTypeGt ) ||
366- p .isType (models .TokenTypeLtEq ) || p .isType (models .TokenTypeGtEq ) {
367- op := p .currentToken .Token .Value
368- p .advance ()
369- // Check for PRIOR on the right side
370- if strings .EqualFold (p .currentToken .Token .Value , "PRIOR" ) {
385+ } else {
386+ // Case 2: col op PRIOR col (PRIOR on the right-hand side)
387+ // or plain expression (no PRIOR)
388+ left , err := p .parsePrimaryExpression ()
389+ if err != nil {
390+ return nil , err
391+ }
392+ if p .isType (models .TokenTypeEq ) || p .isType (models .TokenTypeNeq ) ||
393+ p .isType (models .TokenTypeLt ) || p .isType (models .TokenTypeGt ) ||
394+ p .isType (models .TokenTypeLtEq ) || p .isType (models .TokenTypeGtEq ) {
395+ op := p .currentToken .Token .Value
371396 p .advance ()
372- priorIdent := p .parseIdent ()
373- if priorIdent == nil || priorIdent .Name == "" {
374- return nil , p .expectedError ("column name after PRIOR" )
397+ // Check for PRIOR on the right side
398+ if strings .EqualFold (p .currentToken .Token .Value , "PRIOR" ) {
399+ p .advance ()
400+ priorIdent := p .parseIdent ()
401+ if priorIdent == nil || priorIdent .Name == "" {
402+ return nil , p .expectedError ("column name after PRIOR" )
403+ }
404+ priorExpr := & ast.UnaryExpression {Operator : ast .Prior , Expr : priorIdent }
405+ base = & ast.BinaryExpression {Left : left , Operator : op , Right : priorExpr }
406+ } else {
407+ right , err := p .parsePrimaryExpression ()
408+ if err != nil {
409+ return nil , err
410+ }
411+ base = & ast.BinaryExpression {Left : left , Operator : op , Right : right }
375412 }
376- priorExpr := & ast. UnaryExpression { Operator : ast . Prior , Expr : priorIdent }
377- return & ast. BinaryExpression { Left : left , Operator : op , Right : priorExpr }, nil
413+ } else {
414+ base = left
378415 }
379- right , err := p .parsePrimaryExpression ()
416+ }
417+
418+ // Handle AND/OR chaining for complex conditions like:
419+ // PRIOR id = parent_id AND active = 1
420+ for strings .EqualFold (p .currentToken .Token .Value , "AND" ) ||
421+ strings .EqualFold (p .currentToken .Token .Value , "OR" ) {
422+ logicOp := p .currentToken .Token .Value
423+ p .advance ()
424+ rest , err := p .parseConnectByCondition ()
380425 if err != nil {
381426 return nil , err
382427 }
383- return & ast.BinaryExpression {Left : left , Operator : op , Right : right }, nil
428+ base = & ast.BinaryExpression {Left : base , Operator : logicOp , Right : rest }
384429 }
385- return left , nil
430+
431+ return base , nil
386432}
387433
388434// parsePeriodDefinition parses: PERIOD FOR name (start_col, end_col)
0 commit comments