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
							 | 
						||
| 
								 | 
							
								}
							 |