1package sqlite
2
3import (
4	"errors"
5	"fmt"
6	"regexp"
7	"strings"
8)
9
10type ddl struct {
11	head   string
12	fields []string
13}
14
15func parseDDL(sql string) (*ddl, error) {
16	reg := regexp.MustCompile("(?i)(CREATE TABLE [\"`]?[\\w\\d]+[\"`]?)(?: \\((.*)\\))?")
17	sections := reg.FindStringSubmatch(sql)
18
19	if sections == nil {
20		return nil, errors.New("invalid DDL")
21	}
22
23	ddlHead := sections[1]
24	ddlBody := sections[2]
25	ddlBodyRunes := []rune(ddlBody)
26	fields := []string{}
27
28	bracketLevel := 0
29	var quote rune = 0
30	buf := ""
31
32	for i := 0; i < len(ddlBodyRunes); i++ {
33		c := ddlBodyRunes[i]
34		var next rune = 0
35		if i+1 < len(ddlBodyRunes) {
36			next = ddlBodyRunes[i+1]
37		}
38
39		if c == '\'' || c == '"' || c == '`' {
40			if c == next {
41				// Skip escaped quote
42				buf += string(c)
43				i++
44			} else if quote > 0 {
45				quote = 0
46			} else {
47				quote = c
48			}
49		} else if quote == 0 {
50			if c == '(' {
51				bracketLevel++
52			} else if c == ')' {
53				bracketLevel--
54			} else if bracketLevel == 0 {
55				if c == ',' {
56					fields = append(fields, strings.TrimSpace(buf))
57					buf = ""
58					continue
59				}
60			}
61		}
62
63		if bracketLevel < 0 {
64			return nil, errors.New("invalid DDL, unbalanced brackets")
65		}
66
67		buf += string(c)
68	}
69
70	if bracketLevel != 0 {
71		return nil, errors.New("invalid DDL, unbalanced brackets")
72	}
73
74	if buf != "" {
75		fields = append(fields, strings.TrimSpace(buf))
76	}
77
78	return &ddl{head: ddlHead, fields: fields}, nil
79}
80
81func (d *ddl) compile() string {
82	if len(d.fields) == 0 {
83		return d.head
84	}
85
86	return fmt.Sprintf("%s (%s)", d.head, strings.Join(d.fields, ","))
87}
88
89func (d *ddl) addConstraint(name string, sql string) {
90	reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
91
92	for i := 0; i < len(d.fields); i++ {
93		if reg.MatchString(d.fields[i]) {
94			d.fields[i] = sql
95			return
96		}
97	}
98
99	d.fields = append(d.fields, sql)
100}
101
102func (d *ddl) removeConstraint(name string) bool {
103	reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
104
105	for i := 0; i < len(d.fields); i++ {
106		if reg.MatchString(d.fields[i]) {
107			d.fields = append(d.fields[:i], d.fields[i+1:]...)
108			return true
109		}
110	}
111	return false
112}
113
114func (d *ddl) hasConstraint(name string) bool {
115	reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]")
116
117	for _, f := range d.fields {
118		if reg.MatchString(f) {
119			return true
120		}
121	}
122	return false
123}
124
125func (d *ddl) getColumns() []string {
126	res := []string{}
127
128	for _, f := range d.fields {
129		fUpper := strings.ToUpper(f)
130		if strings.HasPrefix(fUpper, "PRIMARY KEY") ||
131			strings.HasPrefix(fUpper, "CHECK") ||
132			strings.HasPrefix(fUpper, "CONSTRAINT") {
133			continue
134		}
135
136		reg := regexp.MustCompile("^[\"`]?([\\w\\d]+)[\"`]?")
137		match := reg.FindStringSubmatch(f)
138
139		if match != nil {
140			res = append(res, "`"+match[1]+"`")
141		}
142	}
143	return res
144}
145