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