diff --git a/pkg/sql/parser/expressions.go b/pkg/sql/parser/expressions.go index f5513a3d..6b834787 100644 --- a/pkg/sql/parser/expressions.go +++ b/pkg/sql/parser/expressions.go @@ -557,11 +557,6 @@ func (p *Parser) isNumericLiteral() bool { if p.currentToken.ModelType != modelTypeUnset { return p.currentToken.ModelType == models.TokenTypeNumber } - // String fallback for tokens created without ModelType - switch p.currentToken.Type { - case "INT", "NUMBER", "FLOAT": - return true - } return false } @@ -866,7 +861,7 @@ func (p *Parser) parsePrimaryExpression() (ast.Expression, error) { } return nil, goerrors.UnexpectedTokenError( - string(p.currentToken.Type), + p.currentToken.ModelType.String(), p.currentToken.Literal, models.Location{Line: 0, Column: 0}, "", @@ -1183,7 +1178,7 @@ func (p *Parser) parseSubquery() (ast.Statement, error) { return nil, goerrors.ExpectedTokenError( "SELECT or WITH", - string(p.currentToken.Type), + p.currentToken.ModelType.String(), models.Location{Line: 0, Column: 0}, "", ) diff --git a/pkg/sql/parser/parser.go b/pkg/sql/parser/parser.go index 7173e5aa..d0fc2905 100644 --- a/pkg/sql/parser/parser.go +++ b/pkg/sql/parser/parser.go @@ -661,10 +661,23 @@ func NewParser() *Parser { // //lint:ignore SA1019 intentional use during #215 migration func (p *Parser) matchToken(expected token.Type) bool { //nolint:staticcheck // intentional use of deprecated type for Phase 1 bridge - // Convert both to strings for comparison - expectedStr := string(expected) - currentStr := string(p.currentToken.Type) - if currentStr == expectedStr { + if mt, ok := stringTypeToModelType[expected]; ok && mt != models.TokenTypeKeyword { + // Specific ModelType — use fast integer comparison + if p.currentToken.ModelType == mt { + p.advance() + return true + } + // Token may have generic ModelType (e.g., TokenTypeKeyword) if the converter + // didn't assign a specific type. Fall through to string comparison. + if p.currentToken.ModelType == models.TokenTypeKeyword && + strings.EqualFold(string(p.currentToken.Type), string(expected)) { + p.advance() + return true + } + return false + } + // Generic keyword or unmapped token: compare strings + if strings.EqualFold(string(p.currentToken.Type), string(expected)) { p.advance() return true } @@ -984,14 +997,11 @@ func (p *Parser) isIdentifier() bool { // Handles all string token subtypes (single-quoted, dollar-quoted, etc.) // Also handles string fallback for tokens created without ModelType. func (p *Parser) isStringLiteral() bool { - if p.currentToken.ModelType != modelTypeUnset { - switch p.currentToken.ModelType { - case models.TokenTypeString, models.TokenTypeSingleQuotedString, models.TokenTypeDollarQuotedString: - return true - } - return false + switch p.currentToken.ModelType { + case models.TokenTypeString, models.TokenTypeSingleQuotedString, models.TokenTypeDollarQuotedString: + return true } - return p.currentToken.Type == "STRING" + return false } // isComparisonOperator checks if the current token is a comparison operator using O(1) switch. @@ -1028,7 +1038,7 @@ func (p *Parser) isBooleanLiteral() bool { // expectedError returns an error for unexpected token func (p *Parser) expectedError(expected string) error { - return goerrors.ExpectedTokenError(expected, string(p.currentToken.Type), p.currentLocation(), "") + return goerrors.ExpectedTokenError(expected, p.currentToken.ModelType.String(), p.currentLocation(), "") } // parseIdent parses an identifier @@ -1104,15 +1114,20 @@ func (p *Parser) parseTableReference() (*ast.TableReference, error) { // isNonReservedKeyword checks if current token is a non-reserved keyword // that can be used as a table or column name func (p *Parser) isNonReservedKeyword() bool { - // These keywords can be used as table/column names in most SQL dialects - // Check uppercase version of token type for case-insensitive matching - upperType := strings.ToUpper(string(p.currentToken.Type)) - switch upperType { - case "TARGET", "SOURCE", "MATCHED", "VALUE", "NAME", "TYPE", "STATUS": + // These keywords can be used as table/column names in most SQL dialects. + // Use ModelType where possible, with literal fallback for tokens that have + // the generic TokenTypeKeyword. + switch p.currentToken.ModelType { + case models.TokenTypeTarget, models.TokenTypeSource, models.TokenTypeMatched: return true - default: - return false + case models.TokenTypeKeyword: + // Token may have generic ModelType; check literal for specific keywords + switch strings.ToUpper(p.currentToken.Literal) { + case "TARGET", "SOURCE", "MATCHED", "VALUE", "NAME", "TYPE", "STATUS": + return true + } } + return false } // canBeAlias checks if current token can be used as an alias diff --git a/pkg/sql/parser/recovery.go b/pkg/sql/parser/recovery.go index 188797e4..bffa29e6 100644 --- a/pkg/sql/parser/recovery.go +++ b/pkg/sql/parser/recovery.go @@ -77,15 +77,6 @@ func (p *Parser) isStatementStartingKeyword() bool { return true } } - // Fallback: string comparison for tokens without ModelType (e.g., tests) - switch string(p.currentToken.Type) { - case "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP", - "WITH", "MERGE", "REFRESH", "TRUNCATE", - "EXPLAIN", "ANALYZE", "SHOW", "DESCRIBE", "GRANT", "REVOKE", - "SET", "USE", "BEGIN", "COMMIT", "ROLLBACK", "VACUUM", - "CALL", "EXECUTE": - return true - } return false } diff --git a/pkg/sql/parser/select.go b/pkg/sql/parser/select.go index 4573ceda..bd9beafc 100644 --- a/pkg/sql/parser/select.go +++ b/pkg/sql/parser/select.go @@ -18,7 +18,7 @@ func (p *Parser) parseColumnDef() (*ast.ColumnDef, error) { if name == nil { return nil, goerrors.ExpectedTokenError( "column name", - string(p.currentToken.Type), + p.currentToken.ModelType.String(), p.currentLocation(), "", ) @@ -29,7 +29,7 @@ func (p *Parser) parseColumnDef() (*ast.ColumnDef, error) { if dataType == nil { return nil, goerrors.ExpectedTokenError( "data type", - string(p.currentToken.Type), + p.currentToken.ModelType.String(), p.currentLocation(), "", ) @@ -651,7 +651,7 @@ func (p *Parser) parseSelectStatement() (ast.Statement, error) { if !p.isType(models.TokenTypeJoin) { return nil, goerrors.ExpectedTokenError( "JOIN after "+joinType, - string(p.currentToken.Type), + p.currentToken.ModelType.String(), p.currentLocation(), "", ) @@ -705,7 +705,7 @@ func (p *Parser) parseSelectStatement() (ast.Statement, error) { if err != nil { return nil, goerrors.ExpectedTokenError( "table name after "+joinType+" JOIN", - string(p.currentToken.Type), + p.currentToken.ModelType.String(), p.currentLocation(), "", )