140 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Patial Tech.
 | 
						|
// Author, Ankit Patial
 | 
						|
 | 
						|
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
type (
 | 
						|
	TableInfo struct {
 | 
						|
		Schema     string
 | 
						|
		Table      string
 | 
						|
		Columns    []*Column
 | 
						|
		PrimaryKey []string
 | 
						|
	}
 | 
						|
 | 
						|
	Column struct {
 | 
						|
		// Name of column
 | 
						|
		Name string
 | 
						|
		// Type of column
 | 
						|
		Type string
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func parse(scheam []byte) ([]*TableInfo, error) {
 | 
						|
	var (
 | 
						|
		t      = false
 | 
						|
		n      = bytes.Count(scheam, []byte("CREATE TABLE"))
 | 
						|
		tables = make([]*TableInfo, n)
 | 
						|
		i      = 0
 | 
						|
		sb     strings.Builder
 | 
						|
	)
 | 
						|
 | 
						|
	for l := range bytes.SplitSeq(scheam, []byte("\n")) {
 | 
						|
		if strings.HasPrefix(string(l), "CREATE TABLE") {
 | 
						|
			t = true
 | 
						|
			sb.Write(l)
 | 
						|
		} else if t {
 | 
						|
			sb.Write(l)
 | 
						|
			if strings.Contains(string(l), ";") {
 | 
						|
				t = false
 | 
						|
				t, err := parseTableStmt(sb.String())
 | 
						|
				if err != nil {
 | 
						|
					return nil, err
 | 
						|
				}
 | 
						|
				tables[i] = t
 | 
						|
				i++
 | 
						|
				sb.Reset()
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return tables, nil
 | 
						|
}
 | 
						|
 | 
						|
func parseTableStmt(sqlStatement string) (*TableInfo, error) {
 | 
						|
	// Regex to match CREATE TABLE statement and extract table name and column definitions
 | 
						|
	tableRegex := regexp.MustCompile(`(?i)CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:` + "`" + `?(\w+)` + "`" + `?\.)?` + "`" + `?(\w+)` + "`" + `?\s*\(([\s\S]*?)\)(?:\s*ENGINE.*?)?;`)
 | 
						|
	matches := tableRegex.FindStringSubmatch(sqlStatement)
 | 
						|
	if matches == nil {
 | 
						|
		return nil, fmt.Errorf("no CREATE TABLE statement found")
 | 
						|
	}
 | 
						|
 | 
						|
	schema := matches[1]
 | 
						|
	tbl := matches[2]
 | 
						|
	cols := matches[3]
 | 
						|
 | 
						|
	// Parse column definitions by splitting first on commas
 | 
						|
	// This is a simplistic approach - a real SQL parser would be more robust
 | 
						|
	columns := parseColumns(cols)
 | 
						|
 | 
						|
	return &TableInfo{
 | 
						|
		Schema:  schema,
 | 
						|
		Table:   tbl,
 | 
						|
		Columns: columns,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func parseColumns(colsStr string) []*Column {
 | 
						|
	var columns []*Column
 | 
						|
	var currentDef strings.Builder
 | 
						|
	parenthesesCount := 0
 | 
						|
	inQuote := false
 | 
						|
 | 
						|
	// First, intelligently split the column definitions
 | 
						|
	var columnDefs []string
 | 
						|
	for _, char := range colsStr {
 | 
						|
		if char == '(' {
 | 
						|
			parenthesesCount++
 | 
						|
			currentDef.WriteRune(char)
 | 
						|
		} else if char == ')' {
 | 
						|
			parenthesesCount--
 | 
						|
			currentDef.WriteRune(char)
 | 
						|
		} else if char == '\'' || char == '"' {
 | 
						|
			inQuote = !inQuote
 | 
						|
			currentDef.WriteRune(char)
 | 
						|
		} else if char == ',' && parenthesesCount == 0 && !inQuote {
 | 
						|
			// End of a column definition
 | 
						|
			columnDefs = append(columnDefs, strings.TrimSpace(currentDef.String()))
 | 
						|
			currentDef.Reset()
 | 
						|
		} else {
 | 
						|
			currentDef.WriteRune(char)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Add the last column definition if there's anything left
 | 
						|
	if currentDef.Len() > 0 {
 | 
						|
		columnDefs = append(columnDefs, strings.TrimSpace(currentDef.String()))
 | 
						|
	}
 | 
						|
 | 
						|
	// Now parse each column definition
 | 
						|
	for _, colDef := range columnDefs {
 | 
						|
		// Skip constraints and keys that don't define columns
 | 
						|
		if strings.HasPrefix(strings.ToUpper(colDef), "PRIMARY KEY") ||
 | 
						|
			strings.HasPrefix(strings.ToUpper(colDef), "UNIQUE KEY") ||
 | 
						|
			strings.HasPrefix(strings.ToUpper(colDef), "FOREIGN KEY") ||
 | 
						|
			strings.HasPrefix(strings.ToUpper(colDef), "KEY") ||
 | 
						|
			strings.HasPrefix(strings.ToUpper(colDef), "INDEX") ||
 | 
						|
			strings.HasPrefix(strings.ToUpper(colDef), "CONSTRAINT") {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		colNameRegex := regexp.MustCompile(`^` + "`" + `?(\w+)` + "`" + `?\s+(.+)$`)
 | 
						|
		matches := colNameRegex.FindStringSubmatch(colDef)
 | 
						|
		if matches != nil {
 | 
						|
			columns = append(columns, &Column{
 | 
						|
				Name: matches[1],
 | 
						|
				Type: strings.TrimSpace(matches[2]),
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return columns
 | 
						|
}
 |