From e1bdcbd23d8bbff421fae5e6e494b902fc12edfa Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 11:21:48 +0200 Subject: [PATCH 01/18] Update SQLize for the new Sqlite parser --- go.mod | 2 +- go.sum | 2 ++ sql-parser/sqlite.go | 11 ++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d4ca275..e670ecc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/auxten/postgresql-parser v1.0.1 github.com/pingcap/parser v0.0.0-20200623164729-3a18f1e5dceb github.com/pkg/errors v0.9.1 - github.com/rqlite/sql v0.0.0-20240312185922-ffac88a740bd + github.com/rqlite/sql v0.0.0-20241111133259-a4122fabb196 ) require ( diff --git a/go.sum b/go.sum index f1c1982..6ca9548 100644 --- a/go.sum +++ b/go.sum @@ -214,6 +214,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rqlite/sql v0.0.0-20240312185922-ffac88a740bd h1:wW6BtayFoKaaDeIvXRE3SZVPOscSKlYD+X3bB749+zk= github.com/rqlite/sql v0.0.0-20240312185922-ffac88a740bd/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg= +github.com/rqlite/sql v0.0.0-20241111133259-a4122fabb196 h1:SjRKMwKLTEE3STO6unJlz4VlMjMv5NZgIdI9HikBeAc= +github.com/rqlite/sql v0.0.0-20241111133259-a4122fabb196/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index c07ebff..43c1ced 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -17,7 +17,8 @@ func (p *Parser) ParserSqlite(sql string) error { return err } - return sqlite.Walk(p, node) + _, err = sqlite.Walk(p, node) + return err } /* @@ -31,7 +32,7 @@ AlterTableStatement sqlite does not support drop column */ -func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, err error) { +func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err error) { switch n := node.(type) { case *sqlite.CreateTableStatement: tbName := n.Name.String() @@ -128,7 +129,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, err error) { } } - return nil, nil + return nil, nil, nil } func (p *Parser) parseSqliteConstrains(tbName string, conss []sqlite.Constraint) []*ast.ColumnOption { @@ -168,6 +169,6 @@ func (p *Parser) parseSqliteConstrains(tbName string, conss []sqlite.Constraint) return opts } -func (p Parser) VisitEnd(node sqlite.Node) error { - return nil +func (p Parser) VisitEnd(node sqlite.Node) (sqlite.Node, error) { + return nil, nil } From 654bab1f0bf350842bb9c47d86043294a7b64e22 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 11:25:34 +0200 Subject: [PATCH 02/18] Upgrade the build pipeline --- .github/workflows/go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 669d9f8..e76d10b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,4 +1,4 @@ -name: Go +name: Build pipeline on: push: @@ -11,12 +11,12 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version-file: go.mod - name: Build run: go build -v ./... From 1c7643427126c7bbb86228c7714d7be4688dfdd8 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 11:47:23 +0200 Subject: [PATCH 03/18] Add a unit test for parsing sqlite schema --- test/README.md | 3 +++ test/sqlite/data/schema_one_table.sql | 23 ++++++++++++++++++++++ test/sqlite/parser_test.go | 28 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 test/README.md create mode 100644 test/sqlite/data/schema_one_table.sql create mode 100644 test/sqlite/parser_test.go diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..2a446cc --- /dev/null +++ b/test/README.md @@ -0,0 +1,3 @@ +# SQLize tests + +This folder aims to align with the common guidelines of the [golang project layout](https://github.com/golang-standards/project-layout/tree/master/test). diff --git a/test/sqlite/data/schema_one_table.sql b/test/sqlite/data/schema_one_table.sql new file mode 100644 index 0000000..534f316 --- /dev/null +++ b/test/sqlite/data/schema_one_table.sql @@ -0,0 +1,23 @@ +CREATE TABLE IF NOT EXISTS table_with_all_types ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + + name TEXT NOT NULL, + + unique_number INTEGER UNIQUE, + + price REAL, + + is_active BOOLEAN DEFAULT TRUE, + + binary_data BLOB, + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + age INTEGER CHECK (age >= 18), + + description TEXT DEFAULT 'No description', +); + +CREATE INDEX IF NOT EXISTS idx_name ON table_with_all_types (name); + +CREATE INDEX IF NOT EXISTS idx_unique_number_is_active ON table_with_all_types (unique_number, is_active); diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go new file mode 100644 index 0000000..bf04f4d --- /dev/null +++ b/test/sqlite/parser_test.go @@ -0,0 +1,28 @@ +package sqlite + +import ( + "os" + "testing" + + "github.com/sunary/sqlize" +) + +const ( + schemaFileLocation = "data/schema_one_table.sql" +) + +// TestSqliteParser tests that Sqlize can parse a sqlite schema. +func TestSqliteParser(t *testing.T) { + sqlizeCurrent := sqlize.NewSqlize( + sqlize.WithSqlite(), + ) + + schemaSqlBytes, err := os.ReadFile(schemaFileLocation) + if err != nil { + t.Fatalf("failed to read schema file: %v", err) + } + t.Log(string(schemaSqlBytes)) + if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + t.Fatalf("failed to parse schema: %v", err) + } +} From 44de1b0f72ad90a5eb829bdc7cb4cef91c9e3f68 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 12:06:22 +0200 Subject: [PATCH 04/18] Fix the nil error in sqlite parse --- sql-parser/sqlite.go | 19 +++++++++++++++---- test/sqlite/parser_test.go | 1 - 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index 43c1ced..ba91f16 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -1,6 +1,7 @@ package sql_parser import ( + "fmt" "strings" "github.com/pingcap/parser/ast" @@ -49,7 +50,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e }, CurrentAttr: element.SqlAttr{ LiteType: n.Columns[i].Type, - Options: p.parseSqliteConstrains(tbName, n.Columns[i].Constraints), + Options: p.parseSqliteConstrains(tbName, n.Columns[i]), }, } @@ -123,7 +124,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e }, CurrentAttr: element.SqlAttr{ LiteType: n.ColumnDef.Type, - Options: p.parseSqliteConstrains(tbName, n.ColumnDef.Constraints), + Options: p.parseSqliteConstrains(tbName, n.ColumnDef), }, }) } @@ -132,7 +133,8 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e return nil, nil, nil } -func (p *Parser) parseSqliteConstrains(tbName string, conss []sqlite.Constraint) []*ast.ColumnOption { +func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.ColumnDefinition) []*ast.ColumnOption { + conss := columnDefinition.Constraints opts := []*ast.ColumnOption{} for _, cons := range conss { switch cons := cons.(type) { @@ -148,9 +150,18 @@ func (p *Parser) parseSqliteConstrains(tbName string, conss []sqlite.Constraint) indexCol[i] = cons.Columns[i].Collation.Name } + // Unique constraints are sometimes parsed as unnamed. + // If that's the case, we generate the name ourselves + var idxName string + if cons.Name == nil || cons.Name.Name == "" { + idxName = fmt.Sprintf("idx_%s_%s", tbName, strings.Join(indexCol, "_")) + } else { + idxName = cons.Name.Name + } + p.Migration.AddIndex(tbName, element.Index{ Node: element.Node{ - Name: cons.Name.Name, + Name: idxName, Action: element.MigrateAddAction, }, Columns: indexCol, diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go index bf04f4d..bcffa86 100644 --- a/test/sqlite/parser_test.go +++ b/test/sqlite/parser_test.go @@ -21,7 +21,6 @@ func TestSqliteParser(t *testing.T) { if err != nil { t.Fatalf("failed to read schema file: %v", err) } - t.Log(string(schemaSqlBytes)) if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { t.Fatalf("failed to parse schema: %v", err) } From af343d1246a059d1d09448c1151061f5b033cbad Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 12:14:14 +0200 Subject: [PATCH 05/18] FIx the sqlite scripts --- test/sqlite/data/schema_one_table.sql | 2 +- test/sqlite/data/schema_two_tables.sql | 11 +++++++++++ test/sqlite/parser_test.go | 24 ++++++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/sqlite/data/schema_two_tables.sql diff --git a/test/sqlite/data/schema_one_table.sql b/test/sqlite/data/schema_one_table.sql index 534f316..66c4567 100644 --- a/test/sqlite/data/schema_one_table.sql +++ b/test/sqlite/data/schema_one_table.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS table_with_all_types ( age INTEGER CHECK (age >= 18), - description TEXT DEFAULT 'No description', + description TEXT DEFAULT 'No description' ); CREATE INDEX IF NOT EXISTS idx_name ON table_with_all_types (name); diff --git a/test/sqlite/data/schema_two_tables.sql b/test/sqlite/data/schema_two_tables.sql new file mode 100644 index 0000000..568c5a6 --- /dev/null +++ b/test/sqlite/data/schema_two_tables.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS table_a ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS table_b ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + unique_number INTEGER UNIQUE, + a_id INTEGER, + FOREIGN KEY (a_id) REFERENCES table_a(id) +); diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go index bcffa86..f5b1683 100644 --- a/test/sqlite/parser_test.go +++ b/test/sqlite/parser_test.go @@ -8,16 +8,32 @@ import ( ) const ( - schemaFileLocation = "data/schema_one_table.sql" + schemaOneTable = "data/schema_one_table.sql" + schemaTwoTables = "data/schema_two_tables.sql" ) -// TestSqliteParser tests that Sqlize can parse a sqlite schema. -func TestSqliteParser(t *testing.T) { +// TestSqliteParser tests that Sqlize can parse a sqlite schema with one table. +func TestParserSingleTable(t *testing.T) { sqlizeCurrent := sqlize.NewSqlize( sqlize.WithSqlite(), ) - schemaSqlBytes, err := os.ReadFile(schemaFileLocation) + schemaSqlBytes, err := os.ReadFile(schemaOneTable) + if err != nil { + t.Fatalf("failed to read schema file: %v", err) + } + if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + t.Fatalf("failed to parse schema: %v", err) + } +} + +// TestSqliteParser tests that Sqlize can parse a sqlite schema with foreign keys. +func TestParserMultipleTables(t *testing.T) { + sqlizeCurrent := sqlize.NewSqlize( + sqlize.WithSqlite(), + ) + + schemaSqlBytes, err := os.ReadFile(schemaTwoTables) if err != nil { t.Fatalf("failed to read schema file: %v", err) } From de9462c468b5b50c569d0978cd7cc712f4d6a61f Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 12:20:26 +0200 Subject: [PATCH 06/18] Add a unit test for migrations generation --- test/sqlite/consts.go | 6 ++++++ test/sqlite/migration_test.go | 32 ++++++++++++++++++++++++++++++++ test/sqlite/parser_test.go | 5 ----- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 test/sqlite/consts.go create mode 100644 test/sqlite/migration_test.go diff --git a/test/sqlite/consts.go b/test/sqlite/consts.go new file mode 100644 index 0000000..2d84cf8 --- /dev/null +++ b/test/sqlite/consts.go @@ -0,0 +1,6 @@ +package sqlite + +const ( + schemaOneTable = "data/schema_one_table.sql" + schemaTwoTables = "data/schema_two_tables.sql" +) diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go new file mode 100644 index 0000000..c31f7da --- /dev/null +++ b/test/sqlite/migration_test.go @@ -0,0 +1,32 @@ +package sqlite + +import ( + "os" + "testing" + + "github.com/sunary/sqlize" +) + +// TestSqliteParser tests that Sqlize can generate a migration script for the simplest schema. +func TestMigrationGeneratorSingleTable(t *testing.T) { + sqlizeCurrent := sqlize.NewSqlize( + sqlize.WithSqlite(), + ) + + schemaSqlBytes, err := os.ReadFile(schemaOneTable) + if err != nil { + t.Fatalf("failed to read schema file: %v", err) + } + if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + t.Fatalf("failed to parse schema: %v", err) + } + + sqlizeCurrent.StringUp() + // sqlizeCurrent.StringDown() + + // sqlizeCurrent.StringUpWithVersion(0, false) + // sqlizeCurrent.StringDownWithVersion(0) + + // sqlizeCurrent.StringUpWithVersion(123, false) + // sqlizeCurrent.StringDownWithVersion(123) +} diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go index f5b1683..6ed6795 100644 --- a/test/sqlite/parser_test.go +++ b/test/sqlite/parser_test.go @@ -7,11 +7,6 @@ import ( "github.com/sunary/sqlize" ) -const ( - schemaOneTable = "data/schema_one_table.sql" - schemaTwoTables = "data/schema_two_tables.sql" -) - // TestSqliteParser tests that Sqlize can parse a sqlite schema with one table. func TestParserSingleTable(t *testing.T) { sqlizeCurrent := sqlize.NewSqlize( From 49f392fa117645cfa3d9c5ace86c45393e0a1c7a Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 15:11:36 +0200 Subject: [PATCH 07/18] Fix some parse errors, that prevented migration from being generated --- element/column.go | 28 +++++++++++++++++++++++++++ sql-parser/sqlite.go | 26 ++++++++++++++++++------- test/sqlite/data/schema_one_table.sql | 2 +- test/sqlite/migration_test.go | 10 +++++----- 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/element/column.go b/element/column.go index 628d247..61b25d0 100644 --- a/element/column.go +++ b/element/column.go @@ -5,6 +5,7 @@ import ( "crypto/md5" "encoding/hex" "fmt" + "strconv" "strings" ptypes "github.com/auxten/postgresql-parser/pkg/sql/types" @@ -204,6 +205,33 @@ func (c Column) pkDefinition(isPrev bool) (string, bool) { continue } + if sql.IsSqlite() { + if opt.Tp == ast.ColumnOptionDefaultValue { + expression, err := strconv.Unquote(opt.StrValue) + if err != nil { + expression = opt.StrValue + } + if expression[0] == '\'' && expression[len(expression)-1] == '\'' { + // remove single quotes. strconv may not detect it + expression = strconv.Quote(expression[1 : len(expression)-1]) + } + + strSql += " DEFAULT " + expression + continue + } + + if opt.Tp == ast.ColumnOptionAutoIncrement { + strSql += " AUTOINCREMENT" + continue + } + if opt.Tp == ast.ColumnOptionUniqKey { + strSql += " UNIQUE" + continue + } + + // More Sqlite overrides here... + } + if opt.Tp == ast.ColumnOptionReference && opt.Refer == nil { // manual add continue } diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index ba91f16..ac2016e 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -2,6 +2,7 @@ package sql_parser import ( "fmt" + "strconv" "strings" "github.com/pingcap/parser/ast" @@ -134,6 +135,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e } func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.ColumnDefinition) []*ast.ColumnOption { + // https://www.sqlite.org/syntax/column-constraint.html conss := columnDefinition.Constraints opts := []*ast.ColumnOption{} for _, cons := range conss { @@ -145,18 +147,27 @@ func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.C opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionNotNull}) case *sqlite.UniqueConstraint: - indexCol := make([]string, len(cons.Columns)) - for i := range cons.Columns { - indexCol[i] = cons.Columns[i].Collation.Name + var indexCol []string + if len(cons.Columns) == 0 { + indexCol = []string{columnDefinition.Name.Name} + } else { + indexCol := make([]string, len(cons.Columns)) + for i := range cons.Columns { + indexCol[i] = cons.Columns[i].Collation.Name + } } // Unique constraints are sometimes parsed as unnamed. // If that's the case, we generate the name ourselves var idxName string - if cons.Name == nil || cons.Name.Name == "" { - idxName = fmt.Sprintf("idx_%s_%s", tbName, strings.Join(indexCol, "_")) - } else { + if cons.Name != nil && cons.Name.Name != "" { idxName = cons.Name.Name + } else { + tablePart, err := strconv.Unquote(tbName) + if err != nil { + tablePart = tbName + } + idxName = fmt.Sprintf("idx_%s_%s", tablePart, strings.Join(indexCol, "_")) } p.Migration.AddIndex(tbName, element.Index{ @@ -173,7 +184,8 @@ func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.C case *sqlite.DefaultConstraint: opts = append(opts, &ast.ColumnOption{ Tp: ast.ColumnOptionDefaultValue, - StrValue: cons.Default.String()}) + StrValue: cons.Expr.String(), + }) } } diff --git a/test/sqlite/data/schema_one_table.sql b/test/sqlite/data/schema_one_table.sql index 66c4567..6f3e8b1 100644 --- a/test/sqlite/data/schema_one_table.sql +++ b/test/sqlite/data/schema_one_table.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS table_with_all_types ( age INTEGER CHECK (age >= 18), - description TEXT DEFAULT 'No description' + description TEXT DEFAULT "No description" ); CREATE INDEX IF NOT EXISTS idx_name ON table_with_all_types (name); diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index c31f7da..f026ce2 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -22,11 +22,11 @@ func TestMigrationGeneratorSingleTable(t *testing.T) { } sqlizeCurrent.StringUp() - // sqlizeCurrent.StringDown() + sqlizeCurrent.StringDown() - // sqlizeCurrent.StringUpWithVersion(0, false) - // sqlizeCurrent.StringDownWithVersion(0) + sqlizeCurrent.StringUpWithVersion(0, false) + sqlizeCurrent.StringDownWithVersion(0) - // sqlizeCurrent.StringUpWithVersion(123, false) - // sqlizeCurrent.StringDownWithVersion(123) + sqlizeCurrent.StringUpWithVersion(123, false) + sqlizeCurrent.StringDownWithVersion(123) } From 4aade6800bc54685459ea9d2ab884b978593fe0b Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 15:14:49 +0200 Subject: [PATCH 08/18] Add autoincrement keyword --- sql-parser/sqlite.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index ac2016e..08c9da3 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -142,6 +142,9 @@ func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.C switch cons := cons.(type) { case *sqlite.PrimaryKeyConstraint: opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey}) + if cons.Autoincrement.IsValid() { + opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement}) + } case *sqlite.NotNullConstraint: opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionNotNull}) From 28224dcaeb1870dc90b6cba7fe5f1081be48709a Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 15:17:19 +0200 Subject: [PATCH 09/18] Add support for unique constraint --- sql-parser/sqlite.go | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index 08c9da3..7634713 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -1,8 +1,6 @@ package sql_parser import ( - "fmt" - "strconv" "strings" "github.com/pingcap/parser/ast" @@ -150,37 +148,7 @@ func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.C opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionNotNull}) case *sqlite.UniqueConstraint: - var indexCol []string - if len(cons.Columns) == 0 { - indexCol = []string{columnDefinition.Name.Name} - } else { - indexCol := make([]string, len(cons.Columns)) - for i := range cons.Columns { - indexCol[i] = cons.Columns[i].Collation.Name - } - } - - // Unique constraints are sometimes parsed as unnamed. - // If that's the case, we generate the name ourselves - var idxName string - if cons.Name != nil && cons.Name.Name != "" { - idxName = cons.Name.Name - } else { - tablePart, err := strconv.Unquote(tbName) - if err != nil { - tablePart = tbName - } - idxName = fmt.Sprintf("idx_%s_%s", tablePart, strings.Join(indexCol, "_")) - } - - p.Migration.AddIndex(tbName, element.Index{ - Node: element.Node{ - Name: idxName, - Action: element.MigrateAddAction, - }, - Columns: indexCol, - Typ: ast.IndexKeyTypeUnique, - }) + opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey}) case *sqlite.CheckConstraint: From d190ca89f13a91f2fd8168192fa0141e5e6c3755 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 15:21:59 +0200 Subject: [PATCH 10/18] Add support for check constraints --- element/column.go | 4 ++++ sql-parser/sqlite.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/element/column.go b/element/column.go index 61b25d0..a30a444 100644 --- a/element/column.go +++ b/element/column.go @@ -228,6 +228,10 @@ func (c Column) pkDefinition(isPrev bool) (string, bool) { strSql += " UNIQUE" continue } + if opt.Tp == ast.ColumnOptionCheck { + strSql += " CHECK (" + opt.StrValue + ")" + continue + } // More Sqlite overrides here... } diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index 7634713..74da498 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -151,6 +151,10 @@ func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.C opts = append(opts, &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey}) case *sqlite.CheckConstraint: + opts = append(opts, &ast.ColumnOption{ + Tp: ast.ColumnOptionCheck, + StrValue: cons.Expr.String(), + }) case *sqlite.DefaultConstraint: opts = append(opts, &ast.ColumnOption{ From 012d41120e9c38502d9ad923f591a0f758e89c1c Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 16:45:20 +0200 Subject: [PATCH 11/18] Add mention of sqlite constraints --- sql-parser/sqlite.go | 1 + test/sqlite/migration_test.go | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index 74da498..bbe8d21 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -134,6 +134,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.ColumnDefinition) []*ast.ColumnOption { // https://www.sqlite.org/syntax/column-constraint.html + // Also, Sqlite does not support dropping constraints, so we safely can add them here conss := columnDefinition.Constraints opts := []*ast.ColumnOption{} for _, cons := range conss { diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index f026ce2..9985db7 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -21,12 +21,16 @@ func TestMigrationGeneratorSingleTable(t *testing.T) { t.Fatalf("failed to parse schema: %v", err) } - sqlizeCurrent.StringUp() - sqlizeCurrent.StringDown() + runVariousMigrationFunctions(t, sqlizeCurrent) +} + +func runVariousMigrationFunctions(t *testing.T, s *sqlize.Sqlize) { + s.StringUp() + s.StringDown() - sqlizeCurrent.StringUpWithVersion(0, false) - sqlizeCurrent.StringDownWithVersion(0) + s.StringUpWithVersion(0, false) + s.StringDownWithVersion(0) - sqlizeCurrent.StringUpWithVersion(123, false) - sqlizeCurrent.StringDownWithVersion(123) + s.StringUpWithVersion(123, false) + s.StringDownWithVersion(123) } From 0fec06b5bad078e293eb957acf5b6c7fd311db56 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 17:54:53 +0200 Subject: [PATCH 12/18] Fix potential panic --- element/column.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/element/column.go b/element/column.go index a30a444..6c99825 100644 --- a/element/column.go +++ b/element/column.go @@ -206,12 +206,13 @@ func (c Column) pkDefinition(isPrev bool) (string, bool) { } if sql.IsSqlite() { + // SQLite overrides, that pingcap doesn't support if opt.Tp == ast.ColumnOptionDefaultValue { expression, err := strconv.Unquote(opt.StrValue) if err != nil { expression = opt.StrValue } - if expression[0] == '\'' && expression[len(expression)-1] == '\'' { + if len(expression) >= 2 && expression[0] == '\'' && expression[len(expression)-1] == '\'' { // remove single quotes. strconv may not detect it expression = strconv.Quote(expression[1 : len(expression)-1]) } @@ -232,8 +233,6 @@ func (c Column) pkDefinition(isPrev bool) (string, bool) { strSql += " CHECK (" + opt.StrValue + ")" continue } - - // More Sqlite overrides here... } if opt.Tp == ast.ColumnOptionReference && opt.Refer == nil { // manual add From 1588bb56f43045118f850403af45029def9ad4df Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:01:59 +0200 Subject: [PATCH 13/18] Add edge cases for default values to ensure unquote stability for sqlite --- test/sqlite/data/schema_one_table.sql | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/sqlite/data/schema_one_table.sql b/test/sqlite/data/schema_one_table.sql index 6f3e8b1..e57e6c1 100644 --- a/test/sqlite/data/schema_one_table.sql +++ b/test/sqlite/data/schema_one_table.sql @@ -4,8 +4,10 @@ CREATE TABLE IF NOT EXISTS table_with_all_types ( name TEXT NOT NULL, unique_number INTEGER UNIQUE, + number_with_default DEFAULT 123, price REAL, + price_with_default REAL DEFAULT 0.0, is_active BOOLEAN DEFAULT TRUE, @@ -15,7 +17,15 @@ CREATE TABLE IF NOT EXISTS table_with_all_types ( age INTEGER CHECK (age >= 18), - description TEXT DEFAULT "No description" + description TEXT DEFAULT "Some description", + + empty_text TEXT DEFAULT "", + single_char_text TEXT DEFAULT "x", + single_quote_escape TEXT DEFAULT "It\'s a test", + backslash_escape TEXT DEFAULT "C:\\Program Files", + newline_escape TEXT DEFAULT "Line1\nLine2", + tab_escape TEXT DEFAULT "Column1\tColumn2", + unicode_escape TEXT DEFAULT "Unicode: \u263A" ); CREATE INDEX IF NOT EXISTS idx_name ON table_with_all_types (name); From 8ab0e364af332e4fb590c83a9c2db3a8e198342f Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:13:16 +0200 Subject: [PATCH 14/18] Add some migration verification scripts --- test/sqlite/consts.go | 11 +++++++++++ test/sqlite/migration_test.go | 31 +++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/test/sqlite/consts.go b/test/sqlite/consts.go index 2d84cf8..32496cc 100644 --- a/test/sqlite/consts.go +++ b/test/sqlite/consts.go @@ -1,6 +1,17 @@ package sqlite +import ( + "strings" + "testing" +) + const ( schemaOneTable = "data/schema_one_table.sql" schemaTwoTables = "data/schema_two_tables.sql" ) + +func assertContains(t *testing.T, str, substr, message string) { + if !strings.Contains(str, substr) { + t.Errorf("%s: expected to find '%s' in:\n%s", message, substr, str) + } +} diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index 9985db7..191e355 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -1,6 +1,7 @@ package sqlite import ( + "fmt" "os" "testing" @@ -25,12 +26,30 @@ func TestMigrationGeneratorSingleTable(t *testing.T) { } func runVariousMigrationFunctions(t *testing.T, s *sqlize.Sqlize) { - s.StringUp() - s.StringDown() + upSQL := s.StringUp() + downSQL := s.StringDown() - s.StringUpWithVersion(0, false) - s.StringDownWithVersion(0) + // Validate generated migration scripts + assertContains(t, upSQL, "CREATE TABLE", "Up migration should create the table") + assertContains(t, upSQL, "AUTOINCREMENT", "Up migration should include AUTOINCREMENT") + assertContains(t, upSQL, "CHECK (\"age\" >= 18)", "Up migration should include CHECK constraint") + assertContains(t, upSQL, "UNIQUE", "Up migration should include UNIQUE values") + assertContains(t, upSQL, "DEFAULT", "Up migration should include DEFAULT values") - s.StringUpWithVersion(123, false) - s.StringDownWithVersion(123) + assertContains(t, downSQL, "DROP TABLE", "Down migration should drop the table") + + upWithVersionSQL := s.StringUpWithVersion(0, false) + downWithVersionSQL := s.StringDownWithVersion(0) + + // Validate versioned migration scripts + assertContains(t, upWithVersionSQL, "CREATE TABLE IF NOT EXISTS schema_migrations", "Initial migration should create the migrations table") + assertContains(t, downWithVersionSQL, "DROP TABLE IF EXISTS schema_migrations", "Down migration from before initial should drop the migrations table") + + version := s.HashValue() + upWithVersionNumberSQL := s.StringUpWithVersion(version, false) + + // Validate versioned migration scripts + expectedVersionInsert := fmt.Sprintf("INSERT INTO schema_migrations (version, dirty) VALUES (%d, false);", version) + + assertContains(t, upWithVersionNumberSQL, expectedVersionInsert, "Versioned up migration should include version comment") } From ca61ab014e032df3165428e318e377f77d5ef1ed Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:22:40 +0200 Subject: [PATCH 15/18] Refactor tests, extract file read boilerplate --- test/sqlite/common.go | 27 +++++++++++++++++++++++++++ test/sqlite/consts.go | 17 ----------------- test/sqlite/migration_test.go | 8 ++------ test/sqlite/parser_test.go | 15 ++++----------- 4 files changed, 33 insertions(+), 34 deletions(-) create mode 100644 test/sqlite/common.go delete mode 100644 test/sqlite/consts.go diff --git a/test/sqlite/common.go b/test/sqlite/common.go new file mode 100644 index 0000000..50ef6fb --- /dev/null +++ b/test/sqlite/common.go @@ -0,0 +1,27 @@ +package sqlite + +import ( + "os" + "strings" + "testing" +) + +const ( + schemaWithOneTable = "data/schema_one_table.sql" + schemaWithTwoTables = "data/schema_two_tables.sql" +) + +func assertContains(t *testing.T, str, substr, message string) { + if !strings.Contains(str, substr) { + t.Errorf("%s: expected to find '%s' in:\n%s", message, substr, str) + } +} + +func readFile(t *testing.T, path string) string { + t.Helper() + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("Failed to read file %s: %v", path, err) + } + return string(data) +} diff --git a/test/sqlite/consts.go b/test/sqlite/consts.go deleted file mode 100644 index 32496cc..0000000 --- a/test/sqlite/consts.go +++ /dev/null @@ -1,17 +0,0 @@ -package sqlite - -import ( - "strings" - "testing" -) - -const ( - schemaOneTable = "data/schema_one_table.sql" - schemaTwoTables = "data/schema_two_tables.sql" -) - -func assertContains(t *testing.T, str, substr, message string) { - if !strings.Contains(str, substr) { - t.Errorf("%s: expected to find '%s' in:\n%s", message, substr, str) - } -} diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index 191e355..fd5c8b5 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -2,7 +2,6 @@ package sqlite import ( "fmt" - "os" "testing" "github.com/sunary/sqlize" @@ -14,11 +13,8 @@ func TestMigrationGeneratorSingleTable(t *testing.T) { sqlize.WithSqlite(), ) - schemaSqlBytes, err := os.ReadFile(schemaOneTable) - if err != nil { - t.Fatalf("failed to read schema file: %v", err) - } - if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + schemaSql := readFile(t, schemaWithOneTable) + if err := sqlizeCurrent.FromString(schemaSql); err != nil { t.Fatalf("failed to parse schema: %v", err) } diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go index 6ed6795..d7d538c 100644 --- a/test/sqlite/parser_test.go +++ b/test/sqlite/parser_test.go @@ -1,7 +1,6 @@ package sqlite import ( - "os" "testing" "github.com/sunary/sqlize" @@ -13,11 +12,8 @@ func TestParserSingleTable(t *testing.T) { sqlize.WithSqlite(), ) - schemaSqlBytes, err := os.ReadFile(schemaOneTable) - if err != nil { - t.Fatalf("failed to read schema file: %v", err) - } - if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + schemaSql := readFile(t, schemaWithOneTable) + if err := sqlizeCurrent.FromString(schemaSql); err != nil { t.Fatalf("failed to parse schema: %v", err) } } @@ -28,11 +24,8 @@ func TestParserMultipleTables(t *testing.T) { sqlize.WithSqlite(), ) - schemaSqlBytes, err := os.ReadFile(schemaTwoTables) - if err != nil { - t.Fatalf("failed to read schema file: %v", err) - } - if err := sqlizeCurrent.FromString(string(schemaSqlBytes)); err != nil { + schemaSql := readFile(t, schemaWithTwoTables) + if err := sqlizeCurrent.FromString(schemaSql); err != nil { t.Fatalf("failed to parse schema: %v", err) } } From 8512d84237aa30241f4019fd1c2c106305266186 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:37:39 +0200 Subject: [PATCH 16/18] Apply more CodeRabbit suggestions, fix some nitpicks --- sql-parser/sqlite.go | 2 +- test/README.md | 5 +++++ test/sqlite/common.go | 4 ++-- test/sqlite/migration_test.go | 2 +- test/sqlite/parser_test.go | 4 ++-- test/sqlite/{data => testdata}/schema_one_table.sql | 0 test/sqlite/{data => testdata}/schema_two_tables.sql | 0 7 files changed, 11 insertions(+), 6 deletions(-) rename test/sqlite/{data => testdata}/schema_one_table.sql (100%) rename test/sqlite/{data => testdata}/schema_two_tables.sql (100%) diff --git a/sql-parser/sqlite.go b/sql-parser/sqlite.go index bbe8d21..198956a 100644 --- a/sql-parser/sqlite.go +++ b/sql-parser/sqlite.go @@ -129,7 +129,7 @@ func (p *Parser) Visit(node sqlite.Node) (w sqlite.Visitor, n sqlite.Node, err e } } - return nil, nil, nil + return p, nil, nil } func (p *Parser) parseSqliteConstrains(tbName string, columnDefinition *sqlite.ColumnDefinition) []*ast.ColumnOption { diff --git a/test/README.md b/test/README.md index 2a446cc..302ed46 100644 --- a/test/README.md +++ b/test/README.md @@ -1,3 +1,8 @@ # SQLize tests This folder aims to align with the common guidelines of the [golang project layout](https://github.com/golang-standards/project-layout/tree/master/test). + +## How to run? +```bash +go test ./test/... +``` diff --git a/test/sqlite/common.go b/test/sqlite/common.go index 50ef6fb..8f1eb75 100644 --- a/test/sqlite/common.go +++ b/test/sqlite/common.go @@ -7,8 +7,8 @@ import ( ) const ( - schemaWithOneTable = "data/schema_one_table.sql" - schemaWithTwoTables = "data/schema_two_tables.sql" + schemaWithOneTable = "testdata/schema_one_table.sql" + schemaWithTwoTables = "testdata/schema_two_tables.sql" ) func assertContains(t *testing.T, str, substr, message string) { diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index fd5c8b5..efccf81 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -7,7 +7,7 @@ import ( "github.com/sunary/sqlize" ) -// TestSqliteParser tests that Sqlize can generate a migration script for the simplest schema. +// TestMigrationGeneratorSingleTable tests that Sqlize can generate a migration script for the simplest schema. func TestMigrationGeneratorSingleTable(t *testing.T) { sqlizeCurrent := sqlize.NewSqlize( sqlize.WithSqlite(), diff --git a/test/sqlite/parser_test.go b/test/sqlite/parser_test.go index d7d538c..5318e17 100644 --- a/test/sqlite/parser_test.go +++ b/test/sqlite/parser_test.go @@ -6,7 +6,7 @@ import ( "github.com/sunary/sqlize" ) -// TestSqliteParser tests that Sqlize can parse a sqlite schema with one table. +// TestParserSingleTable tests that Sqlize can parse a sqlite schema with one table. func TestParserSingleTable(t *testing.T) { sqlizeCurrent := sqlize.NewSqlize( sqlize.WithSqlite(), @@ -18,7 +18,7 @@ func TestParserSingleTable(t *testing.T) { } } -// TestSqliteParser tests that Sqlize can parse a sqlite schema with foreign keys. +// TestParserMultipleTables tests that Sqlize can parse a sqlite schema with foreign keys. func TestParserMultipleTables(t *testing.T) { sqlizeCurrent := sqlize.NewSqlize( sqlize.WithSqlite(), diff --git a/test/sqlite/data/schema_one_table.sql b/test/sqlite/testdata/schema_one_table.sql similarity index 100% rename from test/sqlite/data/schema_one_table.sql rename to test/sqlite/testdata/schema_one_table.sql diff --git a/test/sqlite/data/schema_two_tables.sql b/test/sqlite/testdata/schema_two_tables.sql similarity index 100% rename from test/sqlite/data/schema_two_tables.sql rename to test/sqlite/testdata/schema_two_tables.sql From 7a3daed4bbbc61a23687940c83a7f970b585527a Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:40:40 +0200 Subject: [PATCH 17/18] Just a couple more finishing improvements by CodeRabbit --- test/sqlite/common.go | 4 ++-- test/sqlite/migration_test.go | 8 ++------ test/sqlite/testdata/schema_one_table.sql | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/test/sqlite/common.go b/test/sqlite/common.go index 8f1eb75..19bed2c 100644 --- a/test/sqlite/common.go +++ b/test/sqlite/common.go @@ -7,8 +7,8 @@ import ( ) const ( - schemaWithOneTable = "testdata/schema_one_table.sql" - schemaWithTwoTables = "testdata/schema_two_tables.sql" + schemaWithOneTable = "./testdata/schema_one_table.sql" + schemaWithTwoTables = "./testdata/schema_two_tables.sql" ) func assertContains(t *testing.T, str, substr, message string) { diff --git a/test/sqlite/migration_test.go b/test/sqlite/migration_test.go index efccf81..3f00cf8 100644 --- a/test/sqlite/migration_test.go +++ b/test/sqlite/migration_test.go @@ -9,19 +9,15 @@ import ( // TestMigrationGeneratorSingleTable tests that Sqlize can generate a migration script for the simplest schema. func TestMigrationGeneratorSingleTable(t *testing.T) { - sqlizeCurrent := sqlize.NewSqlize( + s := sqlize.NewSqlize( sqlize.WithSqlite(), ) schemaSql := readFile(t, schemaWithOneTable) - if err := sqlizeCurrent.FromString(schemaSql); err != nil { + if err := s.FromString(schemaSql); err != nil { t.Fatalf("failed to parse schema: %v", err) } - runVariousMigrationFunctions(t, sqlizeCurrent) -} - -func runVariousMigrationFunctions(t *testing.T, s *sqlize.Sqlize) { upSQL := s.StringUp() downSQL := s.StringDown() diff --git a/test/sqlite/testdata/schema_one_table.sql b/test/sqlite/testdata/schema_one_table.sql index e57e6c1..b2d48a4 100644 --- a/test/sqlite/testdata/schema_one_table.sql +++ b/test/sqlite/testdata/schema_one_table.sql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS table_with_all_types ( name TEXT NOT NULL, unique_number INTEGER UNIQUE, - number_with_default DEFAULT 123, + number_with_default INTEGER DEFAULT 123, price REAL, price_with_default REAL DEFAULT 0.0, From e4a44133a25b21cac5a755e72eb11ff5de038cf0 Mon Sep 17 00:00:00 2001 From: Alexander Shelyugov <62933903+AlexanderShelyugov@users.noreply.github.com> Date: Sat, 17 May 2025 18:54:50 +0200 Subject: [PATCH 18/18] Fix quote/unquote logic for default values --- element/column.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/element/column.go b/element/column.go index 6c99825..21be5db 100644 --- a/element/column.go +++ b/element/column.go @@ -208,15 +208,19 @@ func (c Column) pkDefinition(isPrev bool) (string, bool) { if sql.IsSqlite() { // SQLite overrides, that pingcap doesn't support if opt.Tp == ast.ColumnOptionDefaultValue { + // Parsed StrValue may be quoted in single quotes, which breaks SQL expression. + // We need to unquote it and, if it's a TEXT column. quote it again with double quotes. expression, err := strconv.Unquote(opt.StrValue) if err != nil { expression = opt.StrValue } if len(expression) >= 2 && expression[0] == '\'' && expression[len(expression)-1] == '\'' { // remove single quotes. strconv may not detect it - expression = strconv.Quote(expression[1 : len(expression)-1]) + expression = expression[1 : len(expression)-1] + } + if c.typeDefinition(isPrev) == "TEXT" { + expression = strconv.Quote(expression) } - strSql += " DEFAULT " + expression continue }