@@ -1065,19 +1065,162 @@ func (p *Parser) parseCast() ast.Expression {
10651065 expr .Expr = p .parseExpression (ALIAS_PREC )
10661066
10671067 // Handle both CAST(x AS Type) and CAST(x, 'Type') or CAST(x, expr) syntax
1068+ // Also handle CAST(x AS alias AS Type) and CAST(x alias AS Type) where alias is for the expression
1069+ // And CAST(x AS alias, 'Type') and CAST(x alias, 'Type') for comma-style with aliased expression
10681070 if p .currentIs (token .AS ) {
1069- p .nextToken ()
1071+ p .nextToken () // skip AS
1072+
1073+ // Check what comes after the identifier
1074+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1075+ if p .peekIs (token .AS ) {
1076+ // "AS alias AS Type" pattern
1077+ alias := p .current .Value
1078+ p .nextToken () // skip alias
1079+ p .nextToken () // skip AS
1080+ expr .Expr = p .wrapWithAlias (expr .Expr , alias )
1081+ expr .Type = p .parseDataType ()
1082+ expr .UsedASSyntax = true
1083+ } else if p .peekIs (token .COMMA ) {
1084+ // "AS alias, 'Type'" pattern - comma-style with aliased expression
1085+ alias := p .current .Value
1086+ p .nextToken () // skip alias
1087+ p .nextToken () // skip comma
1088+ expr .Expr = p .wrapWithAlias (expr .Expr , alias )
1089+ // Parse type (which may also have an alias)
1090+ if p .currentIs (token .STRING ) {
1091+ typeStr := p .current .Value
1092+ typePos := p .current .Pos
1093+ p .nextToken ()
1094+ // Check for alias on the type string
1095+ if p .currentIs (token .AS ) {
1096+ p .nextToken ()
1097+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1098+ typeAlias := p .current .Value
1099+ p .nextToken ()
1100+ expr .TypeExpr = & ast.AliasedExpr {
1101+ Position : typePos ,
1102+ Expr : & ast.Literal {Position : typePos , Type : ast .LiteralString , Value : typeStr },
1103+ Alias : typeAlias ,
1104+ }
1105+ } else {
1106+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1107+ }
1108+ } else if p .currentIs (token .IDENT ) && ! p .peekIs (token .LPAREN ) && ! p .peekIs (token .COMMA ) {
1109+ // Implicit alias: cast('1234' AS lhs, 'UInt32' rhs)
1110+ typeAlias := p .current .Value
1111+ p .nextToken ()
1112+ expr .TypeExpr = & ast.AliasedExpr {
1113+ Position : typePos ,
1114+ Expr : & ast.Literal {Position : typePos , Type : ast .LiteralString , Value : typeStr },
1115+ Alias : typeAlias ,
1116+ }
1117+ } else {
1118+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1119+ }
1120+ } else {
1121+ expr .TypeExpr = p .parseExpression (LOWEST )
1122+ }
1123+ } else {
1124+ // Just "AS Type"
1125+ expr .Type = p .parseDataType ()
1126+ expr .UsedASSyntax = true
1127+ }
1128+ } else {
1129+ // Just "AS Type"
1130+ expr .Type = p .parseDataType ()
1131+ expr .UsedASSyntax = true
1132+ }
1133+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .AS ) {
1134+ // Handle "expr alias AS Type" pattern (alias without AS keyword)
1135+ alias := p .current .Value
1136+ p .nextToken () // skip alias
1137+ p .nextToken () // skip AS
1138+ expr .Expr = p .wrapWithAlias (expr .Expr , alias )
10701139 expr .Type = p .parseDataType ()
10711140 expr .UsedASSyntax = true
1141+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .COMMA ) {
1142+ // Handle "expr alias, 'Type'" pattern (alias without AS keyword, comma-style)
1143+ alias := p .current .Value
1144+ p .nextToken () // skip alias
1145+ p .nextToken () // skip comma
1146+ expr .Expr = p .wrapWithAlias (expr .Expr , alias )
1147+ // Parse type (which may also have an alias)
1148+ if p .currentIs (token .STRING ) {
1149+ typeStr := p .current .Value
1150+ typePos := p .current .Pos
1151+ p .nextToken ()
1152+ // Check for alias on the type string
1153+ if p .currentIs (token .AS ) {
1154+ p .nextToken ()
1155+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1156+ typeAlias := p .current .Value
1157+ p .nextToken ()
1158+ expr .TypeExpr = & ast.AliasedExpr {
1159+ Position : typePos ,
1160+ Expr : & ast.Literal {Position : typePos , Type : ast .LiteralString , Value : typeStr },
1161+ Alias : typeAlias ,
1162+ }
1163+ } else {
1164+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1165+ }
1166+ } else if p .currentIs (token .IDENT ) && ! p .peekIs (token .LPAREN ) && ! p .peekIs (token .COMMA ) {
1167+ // Implicit alias: cast('1234' lhs, 'UInt32' rhs)
1168+ typeAlias := p .current .Value
1169+ p .nextToken ()
1170+ expr .TypeExpr = & ast.AliasedExpr {
1171+ Position : typePos ,
1172+ Expr : & ast.Literal {Position : typePos , Type : ast .LiteralString , Value : typeStr },
1173+ Alias : typeAlias ,
1174+ }
1175+ } else {
1176+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1177+ }
1178+ } else {
1179+ expr .TypeExpr = p .parseExpression (LOWEST )
1180+ }
10721181 } else if p .currentIs (token .COMMA ) {
10731182 p .nextToken ()
10741183 // Type can be given as a string literal or an expression (e.g., if(cond, 'Type1', 'Type2'))
1184+ // It can also have an alias like: cast('1234', 'UInt32' AS rhs)
10751185 if p .currentIs (token .STRING ) {
1076- expr .Type = & ast.DataType {
1077- Position : p .current .Pos ,
1078- Name : p .current .Value ,
1079- }
1186+ typeStr := p .current .Value
1187+ typePos := p .current .Pos
10801188 p .nextToken ()
1189+ // Check for alias on the type string
1190+ if p .currentIs (token .AS ) {
1191+ p .nextToken ()
1192+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1193+ alias := p .current .Value
1194+ p .nextToken ()
1195+ // Store as aliased literal in TypeExpr
1196+ expr .TypeExpr = & ast.AliasedExpr {
1197+ Position : typePos ,
1198+ Expr : & ast.Literal {
1199+ Position : typePos ,
1200+ Type : ast .LiteralString ,
1201+ Value : typeStr ,
1202+ },
1203+ Alias : alias ,
1204+ }
1205+ } else {
1206+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1207+ }
1208+ } else if p .currentIs (token .IDENT ) && ! p .peekIs (token .LPAREN ) && ! p .peekIs (token .COMMA ) {
1209+ // Implicit alias (no AS keyword): cast('1234', 'UInt32' rhs)
1210+ alias := p .current .Value
1211+ p .nextToken ()
1212+ expr .TypeExpr = & ast.AliasedExpr {
1213+ Position : typePos ,
1214+ Expr : & ast.Literal {
1215+ Position : typePos ,
1216+ Type : ast .LiteralString ,
1217+ Value : typeStr ,
1218+ },
1219+ Alias : alias ,
1220+ }
1221+ } else {
1222+ expr .Type = & ast.DataType {Position : typePos , Name : typeStr }
1223+ }
10811224 } else {
10821225 // Parse as expression for dynamic type casting
10831226 expr .TypeExpr = p .parseExpression (LOWEST )
@@ -1089,6 +1232,29 @@ func (p *Parser) parseCast() ast.Expression {
10891232 return expr
10901233}
10911234
1235+ // wrapWithAlias wraps an expression with an alias, handling different expression types appropriately
1236+ // If the expression already has an alias (e.g., AliasedExpr), the new alias replaces/overrides it
1237+ func (p * Parser ) wrapWithAlias (expr ast.Expression , alias string ) ast.Expression {
1238+ switch e := expr .(type ) {
1239+ case * ast.Identifier :
1240+ e .Alias = alias
1241+ return e
1242+ case * ast.FunctionCall :
1243+ e .Alias = alias
1244+ return e
1245+ case * ast.AliasedExpr :
1246+ // Replace the alias instead of double-wrapping
1247+ e .Alias = alias
1248+ return e
1249+ default :
1250+ return & ast.AliasedExpr {
1251+ Position : expr .Pos (),
1252+ Expr : expr ,
1253+ Alias : alias ,
1254+ }
1255+ }
1256+ }
1257+
10921258func (p * Parser ) parseExtract () ast.Expression {
10931259 pos := p .current .Pos
10941260 p .nextToken () // skip EXTRACT
@@ -1234,24 +1400,101 @@ func (p *Parser) parseSubstring() ast.Expression {
12341400 return nil
12351401 }
12361402
1237- args := []ast.Expression {p .parseExpression (LOWEST )}
1403+ // Parse first argument (source string) - may have alias before FROM
1404+ // Use ALIAS_PREC to not consume AS
1405+ firstArg := p .parseExpression (ALIAS_PREC )
12381406
1239- // Handle FROM
1407+ // Check for alias on first argument (AS alias or just alias before FROM)
1408+ if p .currentIs (token .AS ) {
1409+ p .nextToken ()
1410+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1411+ alias := p .current .Value
1412+ p .nextToken ()
1413+ firstArg = p .wrapWithAlias (firstArg , alias )
1414+ }
1415+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && (p .peekIs (token .FROM ) || p .peekIs (token .COMMA )) {
1416+ // Implicit alias before FROM or COMMA
1417+ alias := p .current .Value
1418+ p .nextToken ()
1419+ firstArg = p .wrapWithAlias (firstArg , alias )
1420+ }
1421+
1422+ args := []ast.Expression {firstArg }
1423+
1424+ // Handle FROM or COMMA for second argument
12401425 if p .currentIs (token .FROM ) {
12411426 p .nextToken ()
1242- args = append (args , p .parseExpression (LOWEST ))
1427+ // Parse start position - may have alias before FOR or )
1428+ startArg := p .parseExpression (ALIAS_PREC )
1429+ // Check for alias
1430+ if p .currentIs (token .AS ) {
1431+ p .nextToken ()
1432+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1433+ alias := p .current .Value
1434+ p .nextToken ()
1435+ startArg = p .wrapWithAlias (startArg , alias )
1436+ }
1437+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && (p .peekIs (token .FOR ) || p .peekIs (token .RPAREN )) {
1438+ alias := p .current .Value
1439+ p .nextToken ()
1440+ startArg = p .wrapWithAlias (startArg , alias )
1441+ }
1442+ args = append (args , startArg )
12431443 } else if p .currentIs (token .COMMA ) {
12441444 p .nextToken ()
1245- args = append (args , p .parseExpression (LOWEST ))
1445+ // Parse second argument with possible alias
1446+ startArg := p .parseExpression (ALIAS_PREC )
1447+ if p .currentIs (token .AS ) {
1448+ p .nextToken ()
1449+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1450+ alias := p .current .Value
1451+ p .nextToken ()
1452+ startArg = p .wrapWithAlias (startArg , alias )
1453+ }
1454+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && (p .peekIs (token .COMMA ) || p .peekIs (token .RPAREN )) {
1455+ alias := p .current .Value
1456+ p .nextToken ()
1457+ startArg = p .wrapWithAlias (startArg , alias )
1458+ }
1459+ args = append (args , startArg )
12461460 }
12471461
1248- // Handle FOR
1462+ // Handle FOR or COMMA for third argument
12491463 if p .currentIs (token .FOR ) {
12501464 p .nextToken ()
1251- args = append (args , p .parseExpression (LOWEST ))
1465+ // Parse length - may have alias before )
1466+ lenArg := p .parseExpression (ALIAS_PREC )
1467+ // Check for alias
1468+ if p .currentIs (token .AS ) {
1469+ p .nextToken ()
1470+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1471+ alias := p .current .Value
1472+ p .nextToken ()
1473+ lenArg = p .wrapWithAlias (lenArg , alias )
1474+ }
1475+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .RPAREN ) {
1476+ alias := p .current .Value
1477+ p .nextToken ()
1478+ lenArg = p .wrapWithAlias (lenArg , alias )
1479+ }
1480+ args = append (args , lenArg )
12521481 } else if p .currentIs (token .COMMA ) {
12531482 p .nextToken ()
1254- args = append (args , p .parseExpression (LOWEST ))
1483+ // Parse third argument with possible alias
1484+ lenArg := p .parseExpression (ALIAS_PREC )
1485+ if p .currentIs (token .AS ) {
1486+ p .nextToken ()
1487+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1488+ alias := p .current .Value
1489+ p .nextToken ()
1490+ lenArg = p .wrapWithAlias (lenArg , alias )
1491+ }
1492+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .RPAREN ) {
1493+ alias := p .current .Value
1494+ p .nextToken ()
1495+ lenArg = p .wrapWithAlias (lenArg , alias )
1496+ }
1497+ args = append (args , lenArg )
12551498 }
12561499
12571500 p .expect (token .RPAREN )
@@ -1287,15 +1530,43 @@ func (p *Parser) parseTrim() ast.Expression {
12871530 }
12881531
12891532 // Parse characters to trim (if specified)
1533+ // Use ALIAS_PREC to not consume AS as alias
12901534 if ! p .currentIs (token .FROM ) && ! p .currentIs (token .RPAREN ) {
1291- trimChars = p .parseExpression (LOWEST )
1535+ trimChars = p .parseExpression (ALIAS_PREC )
1536+ // Check for alias on trimChars
1537+ if p .currentIs (token .AS ) {
1538+ p .nextToken ()
1539+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1540+ alias := p .current .Value
1541+ p .nextToken ()
1542+ trimChars = p .wrapWithAlias (trimChars , alias )
1543+ }
1544+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .FROM ) {
1545+ alias := p .current .Value
1546+ p .nextToken ()
1547+ trimChars = p .wrapWithAlias (trimChars , alias )
1548+ }
12921549 }
12931550
12941551 // FROM clause
12951552 var expr ast.Expression
12961553 if p .currentIs (token .FROM ) {
12971554 p .nextToken ()
1298- expr = p .parseExpression (LOWEST )
1555+ // Parse expression with possible alias
1556+ expr = p .parseExpression (ALIAS_PREC )
1557+ // Check for alias
1558+ if p .currentIs (token .AS ) {
1559+ p .nextToken ()
1560+ if p .currentIs (token .IDENT ) || p .current .Token .IsKeyword () {
1561+ alias := p .current .Value
1562+ p .nextToken ()
1563+ expr = p .wrapWithAlias (expr , alias )
1564+ }
1565+ } else if (p .currentIs (token .IDENT ) || p .current .Token .IsKeyword ()) && p .peekIs (token .RPAREN ) {
1566+ alias := p .current .Value
1567+ p .nextToken ()
1568+ expr = p .wrapWithAlias (expr , alias )
1569+ }
12991570 } else {
13001571 expr = trimChars
13011572 trimChars = nil
@@ -1310,6 +1581,8 @@ func (p *Parser) parseTrim() ast.Expression {
13101581 fnName = "trimLeft"
13111582 case "TRAILING" :
13121583 fnName = "trimRight"
1584+ case "BOTH" :
1585+ fnName = "trimBoth"
13131586 }
13141587
13151588 args := []ast.Expression {expr }
0 commit comments