You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							408 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							408 lines
						
					
					
						
							11 KiB
						
					
					
				| package engine | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"strings" | |
| 
 | |
| 	"github.com/seaweedfs/cockroachdb-parser/pkg/sql/parser" | |
| 	"github.com/seaweedfs/cockroachdb-parser/pkg/sql/sem/tree" | |
| ) | |
| 
 | |
| // CockroachSQLParser wraps CockroachDB's PostgreSQL-compatible SQL parser for use in SeaweedFS | |
| type CockroachSQLParser struct{} | |
| 
 | |
| // NewCockroachSQLParser creates a new instance of the CockroachDB SQL parser wrapper | |
| func NewCockroachSQLParser() *CockroachSQLParser { | |
| 	return &CockroachSQLParser{} | |
| } | |
| 
 | |
| // ParseSQL parses a SQL statement using CockroachDB's parser | |
| func (p *CockroachSQLParser) ParseSQL(sql string) (Statement, error) { | |
| 	// Parse using CockroachDB's parser | |
| 	stmts, err := parser.Parse(sql) | |
| 	if err != nil { | |
| 		return nil, fmt.Errorf("CockroachDB parser error: %v", err) | |
| 	} | |
| 
 | |
| 	if len(stmts) != 1 { | |
| 		return nil, fmt.Errorf("expected exactly one statement, got %d", len(stmts)) | |
| 	} | |
| 
 | |
| 	stmt := stmts[0].AST | |
| 
 | |
| 	// Convert CockroachDB AST to SeaweedFS AST format | |
| 	switch s := stmt.(type) { | |
| 	case *tree.Select: | |
| 		return p.convertSelectStatement(s) | |
| 	default: | |
| 		return nil, fmt.Errorf("unsupported statement type: %T", s) | |
| 	} | |
| } | |
| 
 | |
| // convertSelectStatement converts CockroachDB's Select AST to SeaweedFS format | |
| func (p *CockroachSQLParser) convertSelectStatement(crdbSelect *tree.Select) (*SelectStatement, error) { | |
| 	selectClause, ok := crdbSelect.Select.(*tree.SelectClause) | |
| 	if !ok { | |
| 		return nil, fmt.Errorf("expected SelectClause, got %T", crdbSelect.Select) | |
| 	} | |
| 
 | |
| 	seaweedSelect := &SelectStatement{ | |
| 		SelectExprs: make([]SelectExpr, 0, len(selectClause.Exprs)), | |
| 		From:        []TableExpr{}, | |
| 	} | |
| 
 | |
| 	// Convert SELECT expressions | |
| 	for _, expr := range selectClause.Exprs { | |
| 		seaweedExpr, err := p.convertSelectExpr(expr) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert select expression: %v", err) | |
| 		} | |
| 		seaweedSelect.SelectExprs = append(seaweedSelect.SelectExprs, seaweedExpr) | |
| 	} | |
| 
 | |
| 	// Convert FROM clause | |
| 	if len(selectClause.From.Tables) > 0 { | |
| 		for _, fromExpr := range selectClause.From.Tables { | |
| 			seaweedTableExpr, err := p.convertFromExpr(fromExpr) | |
| 			if err != nil { | |
| 				return nil, fmt.Errorf("failed to convert FROM clause: %v", err) | |
| 			} | |
| 			seaweedSelect.From = append(seaweedSelect.From, seaweedTableExpr) | |
| 		} | |
| 	} | |
| 
 | |
| 	// Convert WHERE clause if present | |
| 	if selectClause.Where != nil { | |
| 		whereExpr, err := p.convertExpr(selectClause.Where.Expr) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert WHERE clause: %v", err) | |
| 		} | |
| 		seaweedSelect.Where = &WhereClause{ | |
| 			Expr: whereExpr, | |
| 		} | |
| 	} | |
| 
 | |
| 	// Convert LIMIT and OFFSET clauses if present | |
| 	if crdbSelect.Limit != nil { | |
| 		limitClause := &LimitClause{} | |
| 
 | |
| 		// Convert LIMIT (Count) | |
| 		if crdbSelect.Limit.Count != nil { | |
| 			countExpr, err := p.convertExpr(crdbSelect.Limit.Count) | |
| 			if err != nil { | |
| 				return nil, fmt.Errorf("failed to convert LIMIT clause: %v", err) | |
| 			} | |
| 			limitClause.Rowcount = countExpr | |
| 		} | |
| 
 | |
| 		// Convert OFFSET | |
| 		if crdbSelect.Limit.Offset != nil { | |
| 			offsetExpr, err := p.convertExpr(crdbSelect.Limit.Offset) | |
| 			if err != nil { | |
| 				return nil, fmt.Errorf("failed to convert OFFSET clause: %v", err) | |
| 			} | |
| 			limitClause.Offset = offsetExpr | |
| 		} | |
| 
 | |
| 		seaweedSelect.Limit = limitClause | |
| 	} | |
| 
 | |
| 	return seaweedSelect, nil | |
| } | |
| 
 | |
| // convertSelectExpr converts CockroachDB SelectExpr to SeaweedFS format | |
| func (p *CockroachSQLParser) convertSelectExpr(expr tree.SelectExpr) (SelectExpr, error) { | |
| 	// Handle star expressions (SELECT *) | |
| 	if _, isStar := expr.Expr.(tree.UnqualifiedStar); isStar { | |
| 		return &StarExpr{}, nil | |
| 	} | |
| 
 | |
| 	// CockroachDB's SelectExpr is a struct, not an interface, so handle it directly | |
| 	seaweedExpr := &AliasedExpr{} | |
| 
 | |
| 	// Convert the main expression | |
| 	convertedExpr, err := p.convertExpr(expr.Expr) | |
| 	if err != nil { | |
| 		return nil, fmt.Errorf("failed to convert expression: %v", err) | |
| 	} | |
| 	seaweedExpr.Expr = convertedExpr | |
| 
 | |
| 	// Convert alias if present | |
| 	if expr.As != "" { | |
| 		seaweedExpr.As = aliasValue(expr.As) | |
| 	} | |
| 
 | |
| 	return seaweedExpr, nil | |
| } | |
| 
 | |
| // convertExpr converts CockroachDB expressions to SeaweedFS format | |
| func (p *CockroachSQLParser) convertExpr(expr tree.Expr) (ExprNode, error) { | |
| 	switch e := expr.(type) { | |
| 	case *tree.FuncExpr: | |
| 		// Function call | |
| 		seaweedFunc := &FuncExpr{ | |
| 			Name:  stringValue(strings.ToUpper(e.Func.String())), // Convert to uppercase for consistency | |
| 			Exprs: make([]SelectExpr, 0, len(e.Exprs)), | |
| 		} | |
| 
 | |
| 		// Convert function arguments | |
| 		for _, arg := range e.Exprs { | |
| 			// Special case: Handle star expressions in function calls like COUNT(*) | |
| 			if _, isStar := arg.(tree.UnqualifiedStar); isStar { | |
| 				seaweedFunc.Exprs = append(seaweedFunc.Exprs, &StarExpr{}) | |
| 			} else { | |
| 				convertedArg, err := p.convertExpr(arg) | |
| 				if err != nil { | |
| 					return nil, fmt.Errorf("failed to convert function argument: %v", err) | |
| 				} | |
| 				seaweedFunc.Exprs = append(seaweedFunc.Exprs, &AliasedExpr{Expr: convertedArg}) | |
| 			} | |
| 		} | |
| 
 | |
| 		return seaweedFunc, nil | |
| 
 | |
| 	case *tree.BinaryExpr: | |
| 		// Arithmetic/binary operations (including string concatenation ||) | |
| 		seaweedArith := &ArithmeticExpr{ | |
| 			Operator: e.Operator.String(), | |
| 		} | |
| 
 | |
| 		// Convert left operand | |
| 		left, err := p.convertExpr(e.Left) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert left operand: %v", err) | |
| 		} | |
| 		seaweedArith.Left = left | |
| 
 | |
| 		// Convert right operand | |
| 		right, err := p.convertExpr(e.Right) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert right operand: %v", err) | |
| 		} | |
| 		seaweedArith.Right = right | |
| 
 | |
| 		return seaweedArith, nil | |
| 
 | |
| 	case *tree.ComparisonExpr: | |
| 		// Comparison operations (=, >, <, >=, <=, !=, etc.) used in WHERE clauses | |
| 		seaweedComp := &ComparisonExpr{ | |
| 			Operator: e.Operator.String(), | |
| 		} | |
| 
 | |
| 		// Convert left operand | |
| 		left, err := p.convertExpr(e.Left) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert comparison left operand: %v", err) | |
| 		} | |
| 		seaweedComp.Left = left | |
| 
 | |
| 		// Convert right operand | |
| 		right, err := p.convertExpr(e.Right) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert comparison right operand: %v", err) | |
| 		} | |
| 		seaweedComp.Right = right | |
| 
 | |
| 		return seaweedComp, nil | |
| 
 | |
| 	case *tree.StrVal: | |
| 		// String literal | |
| 		return &SQLVal{ | |
| 			Type: StrVal, | |
| 			Val:  []byte(string(e.RawString())), | |
| 		}, nil | |
| 
 | |
| 	case *tree.NumVal: | |
| 		// Numeric literal | |
| 		valStr := e.String() | |
| 		if strings.Contains(valStr, ".") { | |
| 			return &SQLVal{ | |
| 				Type: FloatVal, | |
| 				Val:  []byte(valStr), | |
| 			}, nil | |
| 		} else { | |
| 			return &SQLVal{ | |
| 				Type: IntVal, | |
| 				Val:  []byte(valStr), | |
| 			}, nil | |
| 		} | |
| 
 | |
| 	case *tree.UnresolvedName: | |
| 		// Column name | |
| 		return &ColName{ | |
| 			Name: stringValue(e.String()), | |
| 		}, nil | |
| 
 | |
| 	case *tree.AndExpr: | |
| 		// AND expression | |
| 		left, err := p.convertExpr(e.Left) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert AND left operand: %v", err) | |
| 		} | |
| 		right, err := p.convertExpr(e.Right) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert AND right operand: %v", err) | |
| 		} | |
| 		return &AndExpr{ | |
| 			Left:  left, | |
| 			Right: right, | |
| 		}, nil | |
| 
 | |
| 	case *tree.OrExpr: | |
| 		// OR expression | |
| 		left, err := p.convertExpr(e.Left) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert OR left operand: %v", err) | |
| 		} | |
| 		right, err := p.convertExpr(e.Right) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert OR right operand: %v", err) | |
| 		} | |
| 		return &OrExpr{ | |
| 			Left:  left, | |
| 			Right: right, | |
| 		}, nil | |
| 
 | |
| 	case *tree.Tuple: | |
| 		// Tuple expression for IN clauses: (value1, value2, value3) | |
| 		tupleValues := make(ValTuple, 0, len(e.Exprs)) | |
| 		for _, tupleExpr := range e.Exprs { | |
| 			convertedExpr, err := p.convertExpr(tupleExpr) | |
| 			if err != nil { | |
| 				return nil, fmt.Errorf("failed to convert tuple element: %v", err) | |
| 			} | |
| 			tupleValues = append(tupleValues, convertedExpr) | |
| 		} | |
| 		return tupleValues, nil | |
| 
 | |
| 	case *tree.CastExpr: | |
| 		// Handle INTERVAL expressions: INTERVAL '1 hour' | |
| 		// CockroachDB represents these as cast expressions | |
| 		if p.isIntervalCast(e) { | |
| 			// Extract the string value being cast to interval | |
| 			if strVal, ok := e.Expr.(*tree.StrVal); ok { | |
| 				return &IntervalExpr{ | |
| 					Value: string(strVal.RawString()), | |
| 				}, nil | |
| 			} | |
| 			return nil, fmt.Errorf("invalid INTERVAL expression: expected string literal") | |
| 		} | |
| 		// For non-interval casts, just convert the inner expression | |
| 		return p.convertExpr(e.Expr) | |
| 
 | |
| 	case *tree.RangeCond: | |
| 		// Handle BETWEEN expressions: column BETWEEN value1 AND value2 | |
| 		seaweedBetween := &BetweenExpr{ | |
| 			Not: e.Not, // Handle NOT BETWEEN | |
| 		} | |
| 
 | |
| 		// Convert the left operand (the expression being tested) | |
| 		left, err := p.convertExpr(e.Left) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert BETWEEN left operand: %v", err) | |
| 		} | |
| 		seaweedBetween.Left = left | |
| 
 | |
| 		// Convert the FROM operand (lower bound) | |
| 		from, err := p.convertExpr(e.From) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert BETWEEN from operand: %v", err) | |
| 		} | |
| 		seaweedBetween.From = from | |
| 
 | |
| 		// Convert the TO operand (upper bound) | |
| 		to, err := p.convertExpr(e.To) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert BETWEEN to operand: %v", err) | |
| 		} | |
| 		seaweedBetween.To = to | |
| 
 | |
| 		return seaweedBetween, nil | |
| 
 | |
| 	case *tree.IsNullExpr: | |
| 		// Handle IS NULL expressions: column IS NULL | |
| 		expr, err := p.convertExpr(e.Expr) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert IS NULL expression: %v", err) | |
| 		} | |
| 
 | |
| 		return &IsNullExpr{ | |
| 			Expr: expr, | |
| 		}, nil | |
| 
 | |
| 	case *tree.IsNotNullExpr: | |
| 		// Handle IS NOT NULL expressions: column IS NOT NULL | |
| 		expr, err := p.convertExpr(e.Expr) | |
| 		if err != nil { | |
| 			return nil, fmt.Errorf("failed to convert IS NOT NULL expression: %v", err) | |
| 		} | |
| 
 | |
| 		return &IsNotNullExpr{ | |
| 			Expr: expr, | |
| 		}, nil | |
| 
 | |
| 	default: | |
| 		return nil, fmt.Errorf("unsupported expression type: %T", e) | |
| 	} | |
| } | |
| 
 | |
| // convertFromExpr converts CockroachDB FROM expressions to SeaweedFS format | |
| func (p *CockroachSQLParser) convertFromExpr(expr tree.TableExpr) (TableExpr, error) { | |
| 	switch e := expr.(type) { | |
| 	case *tree.TableName: | |
| 		// Simple table name | |
| 		tableName := TableName{ | |
| 			Name: stringValue(e.Table()), | |
| 		} | |
| 
 | |
| 		// Extract database qualifier if present | |
|  | |
| 		if e.Schema() != "" { | |
| 			tableName.Qualifier = stringValue(e.Schema()) | |
| 		} | |
| 
 | |
| 		return &AliasedTableExpr{ | |
| 			Expr: tableName, | |
| 		}, nil | |
| 
 | |
| 	case *tree.AliasedTableExpr: | |
| 		// Handle aliased table expressions (which is what CockroachDB uses for qualified names) | |
| 		if tableName, ok := e.Expr.(*tree.TableName); ok { | |
| 			seaweedTableName := TableName{ | |
| 				Name: stringValue(tableName.Table()), | |
| 			} | |
| 
 | |
| 			// Extract database qualifier if present | |
| 			if tableName.Schema() != "" { | |
| 				seaweedTableName.Qualifier = stringValue(tableName.Schema()) | |
| 			} | |
| 
 | |
| 			return &AliasedTableExpr{ | |
| 				Expr: seaweedTableName, | |
| 			}, nil | |
| 		} | |
| 
 | |
| 		return nil, fmt.Errorf("unsupported expression in AliasedTableExpr: %T", e.Expr) | |
| 
 | |
| 	default: | |
| 		return nil, fmt.Errorf("unsupported table expression type: %T", e) | |
| 	} | |
| } | |
| 
 | |
| // isIntervalCast checks if a CastExpr is casting to an INTERVAL type | |
| func (p *CockroachSQLParser) isIntervalCast(castExpr *tree.CastExpr) bool { | |
| 	// Check if the target type is an interval type | |
| 	// CockroachDB represents interval types in the Type field | |
| 	// We need to check if it's an interval type by examining the type structure | |
| 	if castExpr.Type != nil { | |
| 		// Try to detect interval type by examining the AST structure | |
| 		// Since we can't easily access the type string, we'll be more conservative | |
| 		// and assume any cast expression on a string literal could be an interval | |
| 		if _, ok := castExpr.Expr.(*tree.StrVal); ok { | |
| 			// This is likely an INTERVAL expression since CockroachDB | |
| 			// represents INTERVAL '1 hour' as casting a string to interval type | |
| 			return true | |
| 		} | |
| 	} | |
| 	return false | |
| }
 |