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.
 
 
 
 
 
 

143 lines
4.8 KiB

package engine
import (
"context"
"fmt"
"strings"
"time"
)
// SQLParserConfig controls which SQL parser to use
type SQLParserConfig struct {
UsePostgreSQLParser bool // If true, use pg_query_go; if false, use mysql-dialect parser
EnableDialectWarnings bool // If true, log warnings about dialect mismatches
}
// DefaultSQLParserConfig returns the default configuration (MySQL parser for now)
func DefaultSQLParserConfig() *SQLParserConfig {
return &SQLParserConfig{
UsePostgreSQLParser: false, // Keep MySQL parser as default for stability
EnableDialectWarnings: true, // Enable warnings about dialect issues
}
}
// PostgreSQLSQLParserConfig returns configuration for PostgreSQL parser
func PostgreSQLSQLParserConfig() *SQLParserConfig {
return &SQLParserConfig{
UsePostgreSQLParser: true,
EnableDialectWarnings: false, // No warnings needed when using correct parser
}
}
// ParseSQL is a unified interface that can use either parser based on configuration
func (config *SQLParserConfig) ParseSQL(sql string) (Statement, error) {
if config.UsePostgreSQLParser {
return config.parseWithPostgreSQL(sql)
}
return config.parseWithMySQL(sql)
}
// parseWithMySQL uses the PostgreSQL parser (fallback for backward compatibility)
func (config *SQLParserConfig) parseWithMySQL(sql string) (Statement, error) {
if config.EnableDialectWarnings {
config.checkForPostgreSQLDialectFeatures(sql)
}
// Since we've removed the MySQL parser, use the PostgreSQL parser instead
// This maintains backward compatibility while using the better parser
return config.parseWithPostgreSQL(sql)
}
// parseWithPostgreSQL uses the new PostgreSQL parser
func (config *SQLParserConfig) parseWithPostgreSQL(sql string) (Statement, error) {
// Use the PostgreSQL parser from engine.go
return ParseSQL(sql)
}
// checkForPostgreSQLDialectFeatures logs warnings for PostgreSQL-specific syntax
func (config *SQLParserConfig) checkForPostgreSQLDialectFeatures(sql string) {
sqlUpper := strings.ToUpper(sql)
// Check for PostgreSQL-specific features
if strings.Contains(sql, "\"") && !strings.Contains(sql, "'") {
fmt.Printf("WARNING: Detected double-quoted identifiers (\") - PostgreSQL uses these, MySQL uses backticks (`)\n")
}
if strings.Contains(sqlUpper, "||") && !strings.Contains(sqlUpper, "CONCAT") {
fmt.Printf("WARNING: Detected || string concatenation - PostgreSQL syntax, MySQL uses CONCAT()\n")
}
if strings.Contains(sqlUpper, "PG_") || strings.Contains(sqlUpper, "INFORMATION_SCHEMA") {
fmt.Printf("WARNING: Detected PostgreSQL system functions/catalogs - may not work with MySQL parser\n")
}
if strings.Contains(sqlUpper, "LIMIT") && strings.Contains(sqlUpper, "OFFSET") {
fmt.Printf("WARNING: LIMIT/OFFSET syntax may differ between PostgreSQL and MySQL\n")
}
}
// SQLEngineWithParser extends SQLEngine with configurable parser
type SQLEngineWithParser struct {
*SQLEngine
ParserConfig *SQLParserConfig
}
// NewSQLEngineWithParser creates a new SQLEngine with parser configuration
func NewSQLEngineWithParser(masterAddr string, config *SQLParserConfig) *SQLEngineWithParser {
if config == nil {
config = DefaultSQLParserConfig()
}
return &SQLEngineWithParser{
SQLEngine: NewSQLEngine(masterAddr),
ParserConfig: config,
}
}
// ExecuteSQL overrides the base ExecuteSQL to use the configured parser
func (e *SQLEngineWithParser) ExecuteSQL(ctx context.Context, sql string) (*QueryResult, error) {
// Clean up the SQL
sql = strings.TrimSpace(sql)
if sql == "" {
return &QueryResult{
Error: fmt.Errorf("empty SQL statement"),
}, fmt.Errorf("empty SQL statement")
}
sqlUpper := strings.ToUpper(sql)
sqlTrimmed := strings.TrimSuffix(strings.TrimSpace(sql), ";")
sqlTrimmed = strings.TrimSpace(sqlTrimmed)
// Handle EXPLAIN as a special case
if strings.HasPrefix(sqlUpper, "EXPLAIN") {
actualSQL := strings.TrimSpace(sql[7:]) // Remove "EXPLAIN" prefix
return e.executeExplain(ctx, actualSQL, time.Now())
}
// Handle DESCRIBE/DESC as a special case since it's not parsed as a standard statement
if strings.HasPrefix(sqlUpper, "DESCRIBE") || strings.HasPrefix(sqlUpper, "DESC") {
return e.handleDescribeCommand(ctx, sqlTrimmed)
}
// Parse the SQL statement using the configured parser
stmt, err := e.ParserConfig.ParseSQL(sql)
if err != nil {
return &QueryResult{
Error: fmt.Errorf("SQL parse error: %v", err),
}, err
}
// Route to appropriate handler based on statement type
// (same logic as the original SQLEngine)
switch stmt := stmt.(type) {
case *ShowStatement:
return e.executeShowStatementWithDescribe(ctx, stmt)
case *DDLStatement:
return e.executeDDLStatement(ctx, stmt)
case *SelectStatement:
return e.executeSelectStatement(ctx, stmt)
default:
err := fmt.Errorf("unsupported SQL statement type: %T", stmt)
return &QueryResult{Error: err}, err
}
}