diff --git a/.golangci.yml b/.golangci.yml index 3d853d3f..64a4f476 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,8 +14,10 @@ linters-settings: linters: enable-all: true disable: + - goerr113 # reform v1 should not wrap errors to keep SemVer compatibility - gomnd # too annoying - lll # too annoying + - nlreturn # too annoying - testpackage # too annoying - wsl # too annoying diff --git a/Makefile b/Makefile index e977bed8..d86177e5 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ env-up-detach: ## Start development environment in the env-down: ## Stop development environment. docker-compose down --volumes --remove-orphans +env-mysql: ## Run mysql client. + docker exec -ti reform_mysql mysql + test: ## Run all tests and gather coverage. make test-unit diff --git a/base_test.go b/base_test.go index cc71e45d..a99c642b 100644 --- a/base_test.go +++ b/base_test.go @@ -268,11 +268,8 @@ func (s *ReformSuite) TestTimezones() { q := fmt.Sprintf(`INSERT INTO projects (id, name, start) VALUES `+ `('11', '11', %s), ('12', '12', %s), ('13', '13', %s), ('14', '14', %s)`, s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4)) - - withIdentityInsert(s.T(), s.q, "people", func() { - _, err := s.q.Exec(q, t1, t2, tVLAT, tHST) - s.NoError(err) - }) + _, err := s.q.Exec(q, t1, t2, tVLAT, tHST) + s.NoError(err) q = `SELECT start, start FROM projects WHERE id IN ('11', '12', '13', '14') ORDER BY id` rows, err := s.q.Query(q) diff --git a/internal/test/models/good.go b/internal/test/models/good.go index 6a3efc4d..080cb7f4 100644 --- a/internal/test/models/good.go +++ b/internal/test/models/good.go @@ -1,3 +1,4 @@ +//nolint:stylecheck package models import ( @@ -106,17 +107,23 @@ type PersonProject struct { ProjectID string `reform:"project_id"` } +// reform:id_only +type IDOnly struct { + ID int32 `reform:"id,pk"` +} + +//reform:constraints +type Constraints struct { + I int32 `reform:"i"` + ID string `reform:"id,pk"` +} + //reform:legacy.people type LegacyPerson struct { ID int32 `reform:"id,pk"` Name *string `reform:"name"` } -// reform:id_only -type IDOnly struct { - ID int32 `reform:"id,pk"` -} - // check interfaces var ( _ reform.BeforeInserter = (*Person)(nil) diff --git a/internal/test/models/good_reform.go b/internal/test/models/good_reform.go index b4007f3e..54966e75 100644 --- a/internal/test/models/good_reform.go +++ b/internal/test/models/good_reform.go @@ -321,102 +321,99 @@ var ( _ fmt.Stringer = (*PersonProject)(nil) ) -type legacyPersonTableType struct { +type iDOnlyTableType struct { s parse.StructInfo z []interface{} } -// Schema returns a schema name in SQL database ("legacy"). -func (v *legacyPersonTableType) Schema() string { +// Schema returns a schema name in SQL database (""). +func (v *iDOnlyTableType) Schema() string { return v.s.SQLSchema } -// Name returns a view or table name in SQL database ("people"). -func (v *legacyPersonTableType) Name() string { +// Name returns a view or table name in SQL database ("id_only"). +func (v *iDOnlyTableType) Name() string { return v.s.SQLName } // Columns returns a new slice of column names for that view or table in SQL database. -func (v *legacyPersonTableType) Columns() []string { - return []string{"id", "name"} +func (v *iDOnlyTableType) Columns() []string { + return []string{"id"} } // NewStruct makes a new struct for that view or table. -func (v *legacyPersonTableType) NewStruct() reform.Struct { - return new(LegacyPerson) +func (v *iDOnlyTableType) NewStruct() reform.Struct { + return new(IDOnly) } // NewRecord makes a new record for that table. -func (v *legacyPersonTableType) NewRecord() reform.Record { - return new(LegacyPerson) +func (v *iDOnlyTableType) NewRecord() reform.Record { + return new(IDOnly) } // PKColumnIndex returns an index of primary key column for that table in SQL database. -func (v *legacyPersonTableType) PKColumnIndex() uint { +func (v *iDOnlyTableType) PKColumnIndex() uint { return uint(v.s.PKFieldIndex) } -// LegacyPersonTable represents people view or table in SQL database. -var LegacyPersonTable = &legacyPersonTableType{ - s: parse.StructInfo{Type: "LegacyPerson", SQLSchema: "legacy", SQLName: "people", Fields: []parse.FieldInfo{{Name: "ID", Type: "int32", Column: "id"}, {Name: "Name", Type: "*string", Column: "name"}}, PKFieldIndex: 0}, - z: new(LegacyPerson).Values(), +// IDOnlyTable represents id_only view or table in SQL database. +var IDOnlyTable = &iDOnlyTableType{ + s: parse.StructInfo{Type: "IDOnly", SQLSchema: "", SQLName: "id_only", Fields: []parse.FieldInfo{{Name: "ID", Type: "int32", Column: "id"}}, PKFieldIndex: 0}, + z: new(IDOnly).Values(), } // String returns a string representation of this struct or record. -func (s LegacyPerson) String() string { - res := make([]string, 2) +func (s IDOnly) String() string { + res := make([]string, 1) res[0] = "ID: " + reform.Inspect(s.ID, true) - res[1] = "Name: " + reform.Inspect(s.Name, true) return strings.Join(res, ", ") } // Values returns a slice of struct or record field values. // Returned interface{} values are never untyped nils. -func (s *LegacyPerson) Values() []interface{} { +func (s *IDOnly) Values() []interface{} { return []interface{}{ s.ID, - s.Name, } } // Pointers returns a slice of pointers to struct or record fields. // Returned interface{} values are never untyped nils. -func (s *LegacyPerson) Pointers() []interface{} { +func (s *IDOnly) Pointers() []interface{} { return []interface{}{ &s.ID, - &s.Name, } } // View returns View object for that struct. -func (s *LegacyPerson) View() reform.View { - return LegacyPersonTable +func (s *IDOnly) View() reform.View { + return IDOnlyTable } // Table returns Table object for that record. -func (s *LegacyPerson) Table() reform.Table { - return LegacyPersonTable +func (s *IDOnly) Table() reform.Table { + return IDOnlyTable } // PKValue returns a value of primary key for that record. // Returned interface{} value is never untyped nil. -func (s *LegacyPerson) PKValue() interface{} { +func (s *IDOnly) PKValue() interface{} { return s.ID } // PKPointer returns a pointer to primary key field for that record. // Returned interface{} value is never untyped nil. -func (s *LegacyPerson) PKPointer() interface{} { +func (s *IDOnly) PKPointer() interface{} { return &s.ID } // HasPK returns true if record has non-zero primary key set, false otherwise. -func (s *LegacyPerson) HasPK() bool { - return s.ID != LegacyPersonTable.z[LegacyPersonTable.s.PKFieldIndex] +func (s *IDOnly) HasPK() bool { + return s.ID != IDOnlyTable.z[IDOnlyTable.s.PKFieldIndex] } // SetPK sets record primary key. -func (s *LegacyPerson) SetPK(pk interface{}) { +func (s *IDOnly) SetPK(pk interface{}) { if i64, ok := pk.(int64); ok { s.ID = int32(i64) } else { @@ -426,106 +423,221 @@ func (s *LegacyPerson) SetPK(pk interface{}) { // check interfaces var ( - _ reform.View = LegacyPersonTable - _ reform.Struct = (*LegacyPerson)(nil) - _ reform.Table = LegacyPersonTable - _ reform.Record = (*LegacyPerson)(nil) - _ fmt.Stringer = (*LegacyPerson)(nil) + _ reform.View = IDOnlyTable + _ reform.Struct = (*IDOnly)(nil) + _ reform.Table = IDOnlyTable + _ reform.Record = (*IDOnly)(nil) + _ fmt.Stringer = (*IDOnly)(nil) ) -type iDOnlyTableType struct { +type constraintsTableType struct { s parse.StructInfo z []interface{} } // Schema returns a schema name in SQL database (""). -func (v *iDOnlyTableType) Schema() string { +func (v *constraintsTableType) Schema() string { return v.s.SQLSchema } -// Name returns a view or table name in SQL database ("id_only"). -func (v *iDOnlyTableType) Name() string { +// Name returns a view or table name in SQL database ("constraints"). +func (v *constraintsTableType) Name() string { return v.s.SQLName } // Columns returns a new slice of column names for that view or table in SQL database. -func (v *iDOnlyTableType) Columns() []string { - return []string{"id"} +func (v *constraintsTableType) Columns() []string { + return []string{"i", "id"} } // NewStruct makes a new struct for that view or table. -func (v *iDOnlyTableType) NewStruct() reform.Struct { - return new(IDOnly) +func (v *constraintsTableType) NewStruct() reform.Struct { + return new(Constraints) } // NewRecord makes a new record for that table. -func (v *iDOnlyTableType) NewRecord() reform.Record { - return new(IDOnly) +func (v *constraintsTableType) NewRecord() reform.Record { + return new(Constraints) } // PKColumnIndex returns an index of primary key column for that table in SQL database. -func (v *iDOnlyTableType) PKColumnIndex() uint { +func (v *constraintsTableType) PKColumnIndex() uint { return uint(v.s.PKFieldIndex) } -// IDOnlyTable represents id_only view or table in SQL database. -var IDOnlyTable = &iDOnlyTableType{ - s: parse.StructInfo{Type: "IDOnly", SQLSchema: "", SQLName: "id_only", Fields: []parse.FieldInfo{{Name: "ID", Type: "int32", Column: "id"}}, PKFieldIndex: 0}, - z: new(IDOnly).Values(), +// ConstraintsTable represents constraints view or table in SQL database. +var ConstraintsTable = &constraintsTableType{ + s: parse.StructInfo{Type: "Constraints", SQLSchema: "", SQLName: "constraints", Fields: []parse.FieldInfo{{Name: "I", Type: "int32", Column: "i"}, {Name: "ID", Type: "string", Column: "id"}}, PKFieldIndex: 1}, + z: new(Constraints).Values(), } // String returns a string representation of this struct or record. -func (s IDOnly) String() string { - res := make([]string, 1) +func (s Constraints) String() string { + res := make([]string, 2) + res[0] = "I: " + reform.Inspect(s.I, true) + res[1] = "ID: " + reform.Inspect(s.ID, true) + return strings.Join(res, ", ") +} + +// Values returns a slice of struct or record field values. +// Returned interface{} values are never untyped nils. +func (s *Constraints) Values() []interface{} { + return []interface{}{ + s.I, + s.ID, + } +} + +// Pointers returns a slice of pointers to struct or record fields. +// Returned interface{} values are never untyped nils. +func (s *Constraints) Pointers() []interface{} { + return []interface{}{ + &s.I, + &s.ID, + } +} + +// View returns View object for that struct. +func (s *Constraints) View() reform.View { + return ConstraintsTable +} + +// Table returns Table object for that record. +func (s *Constraints) Table() reform.Table { + return ConstraintsTable +} + +// PKValue returns a value of primary key for that record. +// Returned interface{} value is never untyped nil. +func (s *Constraints) PKValue() interface{} { + return s.ID +} + +// PKPointer returns a pointer to primary key field for that record. +// Returned interface{} value is never untyped nil. +func (s *Constraints) PKPointer() interface{} { + return &s.ID +} + +// HasPK returns true if record has non-zero primary key set, false otherwise. +func (s *Constraints) HasPK() bool { + return s.ID != ConstraintsTable.z[ConstraintsTable.s.PKFieldIndex] +} + +// SetPK sets record primary key. +func (s *Constraints) SetPK(pk interface{}) { + if i64, ok := pk.(int64); ok { + s.ID = string(i64) + } else { + s.ID = pk.(string) + } +} + +// check interfaces +var ( + _ reform.View = ConstraintsTable + _ reform.Struct = (*Constraints)(nil) + _ reform.Table = ConstraintsTable + _ reform.Record = (*Constraints)(nil) + _ fmt.Stringer = (*Constraints)(nil) +) + +type legacyPersonTableType struct { + s parse.StructInfo + z []interface{} +} + +// Schema returns a schema name in SQL database ("legacy"). +func (v *legacyPersonTableType) Schema() string { + return v.s.SQLSchema +} + +// Name returns a view or table name in SQL database ("people"). +func (v *legacyPersonTableType) Name() string { + return v.s.SQLName +} + +// Columns returns a new slice of column names for that view or table in SQL database. +func (v *legacyPersonTableType) Columns() []string { + return []string{"id", "name"} +} + +// NewStruct makes a new struct for that view or table. +func (v *legacyPersonTableType) NewStruct() reform.Struct { + return new(LegacyPerson) +} + +// NewRecord makes a new record for that table. +func (v *legacyPersonTableType) NewRecord() reform.Record { + return new(LegacyPerson) +} + +// PKColumnIndex returns an index of primary key column for that table in SQL database. +func (v *legacyPersonTableType) PKColumnIndex() uint { + return uint(v.s.PKFieldIndex) +} + +// LegacyPersonTable represents people view or table in SQL database. +var LegacyPersonTable = &legacyPersonTableType{ + s: parse.StructInfo{Type: "LegacyPerson", SQLSchema: "legacy", SQLName: "people", Fields: []parse.FieldInfo{{Name: "ID", Type: "int32", Column: "id"}, {Name: "Name", Type: "*string", Column: "name"}}, PKFieldIndex: 0}, + z: new(LegacyPerson).Values(), +} + +// String returns a string representation of this struct or record. +func (s LegacyPerson) String() string { + res := make([]string, 2) res[0] = "ID: " + reform.Inspect(s.ID, true) + res[1] = "Name: " + reform.Inspect(s.Name, true) return strings.Join(res, ", ") } // Values returns a slice of struct or record field values. // Returned interface{} values are never untyped nils. -func (s *IDOnly) Values() []interface{} { +func (s *LegacyPerson) Values() []interface{} { return []interface{}{ s.ID, + s.Name, } } // Pointers returns a slice of pointers to struct or record fields. // Returned interface{} values are never untyped nils. -func (s *IDOnly) Pointers() []interface{} { +func (s *LegacyPerson) Pointers() []interface{} { return []interface{}{ &s.ID, + &s.Name, } } // View returns View object for that struct. -func (s *IDOnly) View() reform.View { - return IDOnlyTable +func (s *LegacyPerson) View() reform.View { + return LegacyPersonTable } // Table returns Table object for that record. -func (s *IDOnly) Table() reform.Table { - return IDOnlyTable +func (s *LegacyPerson) Table() reform.Table { + return LegacyPersonTable } // PKValue returns a value of primary key for that record. // Returned interface{} value is never untyped nil. -func (s *IDOnly) PKValue() interface{} { +func (s *LegacyPerson) PKValue() interface{} { return s.ID } // PKPointer returns a pointer to primary key field for that record. // Returned interface{} value is never untyped nil. -func (s *IDOnly) PKPointer() interface{} { +func (s *LegacyPerson) PKPointer() interface{} { return &s.ID } // HasPK returns true if record has non-zero primary key set, false otherwise. -func (s *IDOnly) HasPK() bool { - return s.ID != IDOnlyTable.z[IDOnlyTable.s.PKFieldIndex] +func (s *LegacyPerson) HasPK() bool { + return s.ID != LegacyPersonTable.z[LegacyPersonTable.s.PKFieldIndex] } // SetPK sets record primary key. -func (s *IDOnly) SetPK(pk interface{}) { +func (s *LegacyPerson) SetPK(pk interface{}) { if i64, ok := pk.(int64); ok { s.ID = int32(i64) } else { @@ -535,17 +647,18 @@ func (s *IDOnly) SetPK(pk interface{}) { // check interfaces var ( - _ reform.View = IDOnlyTable - _ reform.Struct = (*IDOnly)(nil) - _ reform.Table = IDOnlyTable - _ reform.Record = (*IDOnly)(nil) - _ fmt.Stringer = (*IDOnly)(nil) + _ reform.View = LegacyPersonTable + _ reform.Struct = (*LegacyPerson)(nil) + _ reform.Table = LegacyPersonTable + _ reform.Record = (*LegacyPerson)(nil) + _ fmt.Stringer = (*LegacyPerson)(nil) ) func init() { parse.AssertUpToDate(&PersonTable.s, new(Person)) parse.AssertUpToDate(&ProjectTable.s, new(Project)) parse.AssertUpToDate(&PersonProjectView.s, new(PersonProject)) - parse.AssertUpToDate(&LegacyPersonTable.s, new(LegacyPerson)) parse.AssertUpToDate(&IDOnlyTable.s, new(IDOnly)) + parse.AssertUpToDate(&ConstraintsTable.s, new(Constraints)) + parse.AssertUpToDate(&LegacyPersonTable.s, new(LegacyPerson)) } diff --git a/parse/parse_test.go b/parse/parse_test.go index bd510d5a..8346f864 100644 --- a/parse/parse_test.go +++ b/parse/parse_test.go @@ -13,6 +13,7 @@ import ( . "gopkg.in/reform.v1/parse" ) +//nolint:gochecknoglobals var ( person = StructInfo{ Type: "Person", @@ -50,15 +51,14 @@ var ( PKFieldIndex: -1, } - legacyPerson = StructInfo{ - Type: "LegacyPerson", - SQLSchema: "legacy", - SQLName: "people", + constraints = StructInfo{ + Type: "Constraints", + SQLName: "constraints", Fields: []FieldInfo{ - {Name: "ID", Type: "int32", Column: "id"}, - {Name: "Name", Type: "*string", Column: "name"}, + {Name: "I", Type: "int32", Column: "i"}, + {Name: "ID", Type: "string", Column: "id"}, }, - PKFieldIndex: 0, + PKFieldIndex: 1, } idOnly = StructInfo{ @@ -70,6 +70,17 @@ var ( PKFieldIndex: 0, } + legacyPerson = StructInfo{ + Type: "LegacyPerson", + SQLSchema: "legacy", + SQLName: "people", + Fields: []FieldInfo{ + {Name: "ID", Type: "int32", Column: "id"}, + {Name: "Name", Type: "*string", Column: "name"}, + }, + PKFieldIndex: 0, + } + extra = StructInfo{ Type: "Extra", SQLName: "extra", @@ -103,12 +114,13 @@ var ( func TestFileGood(t *testing.T) { s, err := File(filepath.FromSlash("../internal/test/models/good.go")) assert.NoError(t, err) - require.Len(t, s, 5) + require.Len(t, s, 6) assert.Equal(t, person, s[0]) assert.Equal(t, project, s[1]) assert.Equal(t, personProject, s[2]) - assert.Equal(t, legacyPerson, s[3]) - assert.Equal(t, idOnly, s[4]) + assert.Equal(t, idOnly, s[3]) + assert.Equal(t, constraints, s[4]) + assert.Equal(t, legacyPerson, s[5]) } func TestFileExtra(t *testing.T) { @@ -156,13 +168,17 @@ func TestObjectGood(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &personProject, s) - s, err = Object(new(models.LegacyPerson), "legacy", "people") - assert.NoError(t, err) - assert.Equal(t, &legacyPerson, s) - s, err = Object(new(models.IDOnly), "", "id_only") assert.NoError(t, err) assert.Equal(t, &idOnly, s) + + s, err = Object(new(models.Constraints), "", "constraints") + assert.NoError(t, err) + assert.Equal(t, &constraints, s) + + s, err = Object(new(models.LegacyPerson), "legacy", "people") + assert.NoError(t, err) + assert.Equal(t, &legacyPerson, s) } func TestObjectExtra(t *testing.T) { @@ -210,13 +226,17 @@ func TestHelpersGood(t *testing.T) { assert.Equal(t, []string{"person_id", "project_id"}, personProject.Columns()) assert.False(t, personProject.IsTable()) - assert.Equal(t, []string{"id", "name"}, legacyPerson.Columns()) - assert.True(t, legacyPerson.IsTable()) - assert.Equal(t, FieldInfo{Name: "ID", Type: "int32", Column: "id"}, legacyPerson.PKField()) + assert.Equal(t, []string{"i", "id"}, constraints.Columns()) + assert.True(t, constraints.IsTable()) + assert.Equal(t, FieldInfo{Name: "ID", Type: "string", Column: "id"}, constraints.PKField()) assert.Equal(t, []string{"id"}, idOnly.Columns()) assert.True(t, idOnly.IsTable()) assert.Equal(t, FieldInfo{Name: "ID", Type: "int32", Column: "id"}, idOnly.PKField()) + + assert.Equal(t, []string{"id", "name"}, legacyPerson.Columns()) + assert.True(t, legacyPerson.IsTable()) + assert.Equal(t, FieldInfo{Name: "ID", Type: "int32", Column: "id"}, legacyPerson.PKField()) } func TestHelpersExtra(t *testing.T) { diff --git a/reform-db/cmd_init.go b/reform-db/cmd_init.go index f843453b..07f623f8 100644 --- a/reform-db/cmd_init.go +++ b/reform-db/cmd_init.go @@ -80,7 +80,10 @@ func convertName(sqlName string) string { // getPrimaryKeyColumn returns single primary key column for given table, or nil. func getPrimaryKeyColumn(db *reform.DB, catalog, schema, tableName string) *keyColumnUsage { - using := []string{"table_catalog", "table_schema", "table_name"} + using := []string{ + "table_catalog", "table_schema", "table_name", + "constraint_catalog", "constraint_schema", "constraint_name", + } if db.Dialect == mysql.Dialect { // MySQL doesn't have table_catalog in table_constraints using = using[1:] @@ -98,22 +101,40 @@ func getPrimaryKeyColumn(db *reform.DB, catalog, schema, tableName string) *keyC ORDER BY ordinal_position DESC`, strings.Join(using, " AND "), db.Placeholder(1), db.Placeholder(2), db.Placeholder(3), ) - row := db.QueryRow(q, catalog, schema, tableName) + rows, err := db.Query(q, catalog, schema, tableName) + if err != nil { + logger.Fatalf("%s", err) + } + defer rows.Close() //nolint:errcheck + var key keyColumnUsage - if err := row.Scan(key.Pointers()...); err != nil { - if err == reform.ErrNoRows { - return nil + var count int + for { + if err = db.NextRow(&key, rows); err != nil { + break } + count++ + logger.Debugf("%s", key) + } + if err != reform.ErrNoRows { logger.Fatalf("%s", err) } - if key.OrdinalPosition > 1 { - logger.Printf( - "Composite primary keys are not supported (found %d columns), skipping it for table %s.", - key.OrdinalPosition, tableName, - ) + switch count { + case 0: + return nil + case 1: + if key.OrdinalPosition > 1 { + logger.Printf( + "Composite primary keys are not supported (found %d columns), skipping it for table %s.", + key.OrdinalPosition, tableName, + ) + return nil + } + return &key + default: + logger.Fatalf("Too many rows found. Please report this bug.") return nil } - return &key } // initModelsInformationSchema returns structs from database with information_schema. diff --git a/reform-db/cmd_init_test.go b/reform-db/cmd_init_test.go index d9afe0f7..72afadb6 100644 --- a/reform-db/cmd_init_test.go +++ b/reform-db/cmd_init_test.go @@ -13,12 +13,13 @@ import ( func (s *ReformDBSuite) TestInit() { good, err := parse.File("../internal/test/models/good.go") s.Require().NoError(err) - s.Require().Len(good, 5) + s.Require().Len(good, 6) people := good[0] projects := good[1] personProject := good[2] - idOnly := good[4] + idOnly := good[3] + constraints := good[4] // patch difference we don't handle people.Type = strings.Replace(people.Type, "Person", "People", -1) @@ -28,6 +29,7 @@ func (s *ReformDBSuite) TestInit() { people.Fields[1].Type = strings.Replace(people.Fields[1].Type, "int32", "int64", -1) personProject.Fields[0].Type = strings.Replace(personProject.Fields[0].Type, "int32", "int64", -1) idOnly.Fields[0].Type = strings.Replace(idOnly.Fields[0].Type, "int32", "int64", -1) + constraints.Fields[0].Type = strings.Replace(constraints.Fields[0].Type, "int32", "int64", -1) } dir, err := ioutil.TempDir("", "ReformDBTestInit") @@ -38,7 +40,7 @@ func (s *ReformDBSuite) TestInit() { fis, err := ioutil.ReadDir(dir) s.Require().NoError(err) - s.Require().Len(fis, 4) + s.Require().Len(fis, 5) ff := filepath.Join(dir, "people.go") actual, err := parse.File(ff) @@ -64,6 +66,12 @@ func (s *ReformDBSuite) TestInit() { s.Require().Len(actual, 1) s.Require().Equal(idOnly, actual[0]) + ff = filepath.Join(dir, "constraints.go") + actual, err = parse.File(ff) + s.Require().NoError(err) + s.Require().Len(actual, 1) + s.Require().Equal(constraints, actual[0]) + err = os.RemoveAll(dir) s.Require().NoError(err) } diff --git a/test/sql/mssql_init.sql b/test/sql/mssql_init.sql index 881e8e1b..0ce917fc 100644 --- a/test/sql/mssql_init.sql +++ b/test/sql/mssql_init.sql @@ -24,5 +24,11 @@ CREATE TABLE id_only ( [id] int identity(1, 1) PRIMARY KEY ); +CREATE TABLE constraints ( + [i] int identity(1, 1) NOT NULL, + [id] varchar(255) PRIMARY KEY, + UNIQUE ([i]) +); + -- to allow insert test data with IDs SET IDENTITY_INSERT people ON; diff --git a/test/sql/mysql_init.sql b/test/sql/mysql_init.sql index 7f38c77b..b21b73df 100644 --- a/test/sql/mysql_init.sql +++ b/test/sql/mysql_init.sql @@ -36,3 +36,10 @@ CREATE TABLE id_only ( id int NOT NULL AUTO_INCREMENT, PRIMARY KEY (id) ); + +CREATE TABLE constraints ( + i int NOT NULL AUTO_INCREMENT, + id varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE (i) +); diff --git a/test/sql/postgres_init.sql b/test/sql/postgres_init.sql index 8f497101..7e5831c6 100644 --- a/test/sql/postgres_init.sql +++ b/test/sql/postgres_init.sql @@ -26,6 +26,12 @@ CREATE TABLE id_only ( id serial PRIMARY KEY ); +CREATE TABLE constraints ( + i serial, + id varchar PRIMARY KEY, + UNIQUE (i) +); + CREATE SCHEMA legacy; CREATE TABLE legacy.people ( diff --git a/test/sql/sqlite3_init.sql b/test/sql/sqlite3_init.sql index 2b969b09..d876bd05 100644 --- a/test/sql/sqlite3_init.sql +++ b/test/sql/sqlite3_init.sql @@ -23,3 +23,9 @@ CREATE TABLE person_project ( CREATE TABLE id_only ( id integer NOT NULL PRIMARY KEY AUTOINCREMENT ); + +CREATE TABLE constraints ( + i integer NOT NULL, -- no AUTOINCREMENT - it works only for PRIMARY KEY: https://www.sqlite.org/autoinc.html + id varchar NOT NULL PRIMARY KEY, + UNIQUE (i) +);