mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Updated GORM driver.
Fixes https://github.com/go-gorm/sqlite/issues/192.
This commit is contained in:
@@ -17,23 +17,11 @@ var (
|
|||||||
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
|
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
|
||||||
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
|
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
|
||||||
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
|
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
|
||||||
columnsRegexp = regexp.MustCompile(fmt.Sprintf(`[(,][%v]?(\w+)[%v]?`, sqliteSeparator, sqliteSeparator))
|
|
||||||
columnRegexp = regexp.MustCompile(fmt.Sprintf(`^[%v]?([\w\d]+)[%v]?\s+([\w\(\)\d]+)(.*)$`, sqliteSeparator, sqliteSeparator))
|
columnRegexp = regexp.MustCompile(fmt.Sprintf(`^[%v]?([\w\d]+)[%v]?\s+([\w\(\)\d]+)(.*)$`, sqliteSeparator, sqliteSeparator))
|
||||||
defaultValueRegexp = regexp.MustCompile(`(?i) DEFAULT \(?(.+)?\)?( |COLLATE|GENERATED|$)`)
|
defaultValueRegexp = regexp.MustCompile(`(?i) DEFAULT \(?(.+)?\)?( |COLLATE|GENERATED|$)`)
|
||||||
regRealDataType = regexp.MustCompile(`[^\d](\d+)[^\d]?`)
|
regRealDataType = regexp.MustCompile(`[^\d](\d+)[^\d]?`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAllColumns(s string) []string {
|
|
||||||
allMatches := columnsRegexp.FindAllStringSubmatch(s, -1)
|
|
||||||
columns := make([]string, 0, len(allMatches))
|
|
||||||
for _, matches := range allMatches {
|
|
||||||
if len(matches) > 1 {
|
|
||||||
columns = append(columns, matches[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return columns
|
|
||||||
}
|
|
||||||
|
|
||||||
type ddl struct {
|
type ddl struct {
|
||||||
head string
|
head string
|
||||||
fields []string
|
fields []string
|
||||||
@@ -110,9 +98,10 @@ func parseDDL(strs ...string) (*ddl, error) {
|
|||||||
if strings.HasPrefix(fUpper, "CONSTRAINT") {
|
if strings.HasPrefix(fUpper, "CONSTRAINT") {
|
||||||
matches := uniqueRegexp.FindStringSubmatch(f)
|
matches := uniqueRegexp.FindStringSubmatch(f)
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
if columns := getAllColumns(matches[1]); len(columns) == 1 {
|
cols, err := parseAllColumns(matches[1])
|
||||||
|
if err == nil && len(cols) == 1 {
|
||||||
for idx, column := range result.columns {
|
for idx, column := range result.columns {
|
||||||
if column.NameValue.String == columns[0] {
|
if column.NameValue.String == cols[0] {
|
||||||
column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
|
column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
|
||||||
result.columns[idx] = column
|
result.columns[idx] = column
|
||||||
break
|
break
|
||||||
@@ -123,12 +112,15 @@ func parseDDL(strs ...string) (*ddl, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
|
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
|
||||||
for _, name := range getAllColumns(f) {
|
cols, err := parseAllColumns(f)
|
||||||
for idx, column := range result.columns {
|
if err == nil {
|
||||||
if column.NameValue.String == name {
|
for _, name := range cols {
|
||||||
column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
|
for idx, column := range result.columns {
|
||||||
result.columns[idx] = column
|
if column.NameValue.String == name {
|
||||||
break
|
column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
|
||||||
|
result.columns[idx] = column
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
117
gormlite/ddlmod_parse_all_columns.go
Normal file
117
gormlite/ddlmod_parse_all_columns.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package gormlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type parseAllColumnsState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
parseAllColumnsState_NONE parseAllColumnsState = iota
|
||||||
|
parseAllColumnsState_Beginning
|
||||||
|
parseAllColumnsState_ReadingRawName
|
||||||
|
parseAllColumnsState_ReadingQuotedName
|
||||||
|
parseAllColumnsState_EndOfName
|
||||||
|
parseAllColumnsState_State_End
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseAllColumns(in string) ([]string, error) {
|
||||||
|
s := []rune(in)
|
||||||
|
columns := make([]string, 0)
|
||||||
|
state := parseAllColumnsState_NONE
|
||||||
|
quote := rune(0)
|
||||||
|
name := make([]rune, 0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch state {
|
||||||
|
case parseAllColumnsState_NONE:
|
||||||
|
if s[i] == '(' {
|
||||||
|
state = parseAllColumnsState_Beginning
|
||||||
|
}
|
||||||
|
case parseAllColumnsState_Beginning:
|
||||||
|
if isSpace(s[i]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isQuote(s[i]) {
|
||||||
|
state = parseAllColumnsState_ReadingQuotedName
|
||||||
|
quote = s[i]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s[i] == '[' {
|
||||||
|
state = parseAllColumnsState_ReadingQuotedName
|
||||||
|
quote = ']'
|
||||||
|
continue
|
||||||
|
} else if s[i] == ')' {
|
||||||
|
return columns, fmt.Errorf("unexpected token: %s", string(s[i]))
|
||||||
|
}
|
||||||
|
state = parseAllColumnsState_ReadingRawName
|
||||||
|
name = append(name, s[i])
|
||||||
|
case parseAllColumnsState_ReadingRawName:
|
||||||
|
if isSeparator(s[i]) {
|
||||||
|
state = parseAllColumnsState_Beginning
|
||||||
|
columns = append(columns, string(name))
|
||||||
|
name = make([]rune, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s[i] == ')' {
|
||||||
|
state = parseAllColumnsState_State_End
|
||||||
|
columns = append(columns, string(name))
|
||||||
|
}
|
||||||
|
if isQuote(s[i]) {
|
||||||
|
return nil, fmt.Errorf("unexpected token: %s", string(s[i]))
|
||||||
|
}
|
||||||
|
if isSpace(s[i]) {
|
||||||
|
state = parseAllColumnsState_EndOfName
|
||||||
|
columns = append(columns, string(name))
|
||||||
|
name = make([]rune, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = append(name, s[i])
|
||||||
|
case parseAllColumnsState_ReadingQuotedName:
|
||||||
|
if s[i] == quote {
|
||||||
|
// check if quote character is escaped
|
||||||
|
if i+1 < len(s) && s[i+1] == quote {
|
||||||
|
name = append(name, quote)
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state = parseAllColumnsState_EndOfName
|
||||||
|
columns = append(columns, string(name))
|
||||||
|
name = make([]rune, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name = append(name, s[i])
|
||||||
|
case parseAllColumnsState_EndOfName:
|
||||||
|
if isSpace(s[i]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isSeparator(s[i]) {
|
||||||
|
state = parseAllColumnsState_Beginning
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s[i] == ')' {
|
||||||
|
state = parseAllColumnsState_State_End
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected token: %s", string(s[i]))
|
||||||
|
case parseAllColumnsState_State_End:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if state != parseAllColumnsState_State_End {
|
||||||
|
return nil, errors.New("unexpected end")
|
||||||
|
}
|
||||||
|
return columns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(r rune) bool {
|
||||||
|
return r == ' ' || r == '\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQuote(r rune) bool {
|
||||||
|
return r == '`' || r == '"' || r == '\''
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSeparator(r rune) bool {
|
||||||
|
return r == ','
|
||||||
|
}
|
||||||
48
gormlite/ddlmod_parse_all_columns_test.go
Normal file
48
gormlite/ddlmod_parse_all_columns_test.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package gormlite
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParseAllColumns(t *testing.T) {
|
||||||
|
tc := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Simple case",
|
||||||
|
input: "PRIMARY KEY (column1, column2)",
|
||||||
|
expected: []string{"column1", "column2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Quoted column name",
|
||||||
|
input: "PRIMARY KEY (`column,xxx`, \"column 2\", \"column)3\", 'column''4', \"column\"\"5\")",
|
||||||
|
expected: []string{"column,xxx", "column 2", "column)3", "column'4", "column\"5"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Japanese column name",
|
||||||
|
input: "PRIMARY KEY (カラム1, `カラム2`)",
|
||||||
|
expected: []string{"カラム1", "カラム2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Column name quoted with []",
|
||||||
|
input: "PRIMARY KEY ([column1], [column2])",
|
||||||
|
expected: []string{"column1", "column2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tc {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cols, err := parseAllColumns(tt.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to parse columns: %s", err)
|
||||||
|
}
|
||||||
|
if len(cols) != len(tt.expected) {
|
||||||
|
t.Errorf("Expected %d columns, got %d", len(tt.expected), len(cols))
|
||||||
|
}
|
||||||
|
for i, col := range cols {
|
||||||
|
if col != tt.expected[i] {
|
||||||
|
t.Errorf("Expected %s, got %s", tt.expected[i], col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,13 @@ set -euo pipefail
|
|||||||
|
|
||||||
cd -P -- "$(dirname -- "$0")"
|
cd -P -- "$(dirname -- "$0")"
|
||||||
|
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/ddlmod.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/ddlmod.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/ddlmod_test.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/ddlmod_test.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/error_translator.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/ddlmod_parse_all_columns.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/migrator.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/ddlmod_parse_all_columns_test.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/sqlite.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/error_translator.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/sqlite_test.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/migrator.go"
|
||||||
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.6/sqlite_test.go"
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/sqlite.go"
|
||||||
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/sqlite_test.go"
|
||||||
|
curl -#OL "https://github.com/go-gorm/sqlite/raw/v1.5.7/sqlite_test.go"
|
||||||
curl -#L "https://github.com/glebarez/sqlite/raw/v1.11.0/sqlite_error_translator_test.go" > error_translator_test.go
|
curl -#L "https://github.com/glebarez/sqlite/raw/v1.11.0/sqlite_error_translator_test.go" > error_translator_test.go
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3"
|
"github.com/ncruces/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Translate it will translate the error to native gorm errors.
|
||||||
func (_Dialector) Translate(err error) error {
|
func (_Dialector) Translate(err error) error {
|
||||||
switch {
|
switch {
|
||||||
case
|
case
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type _Dialector struct {
|
||||||
|
DSN string
|
||||||
|
Conn gorm.ConnPool
|
||||||
|
}
|
||||||
|
|
||||||
// Open opens a GORM dialector from a data source name.
|
// Open opens a GORM dialector from a data source name.
|
||||||
func Open(dsn string) gorm.Dialector {
|
func Open(dsn string) gorm.Dialector {
|
||||||
return &_Dialector{DSN: dsn}
|
return &_Dialector{DSN: dsn}
|
||||||
@@ -24,11 +29,6 @@ func OpenDB(db gorm.ConnPool) gorm.Dialector {
|
|||||||
return &_Dialector{Conn: db}
|
return &_Dialector{Conn: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
type _Dialector struct {
|
|
||||||
DSN string
|
|
||||||
Conn gorm.ConnPool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dialector _Dialector) Name() string {
|
func (dialector _Dialector) Name() string {
|
||||||
return "sqlite"
|
return "sqlite"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user