├── integration_test ├── doc.go ├── main_test.go └── scenario_test.go ├── .travis.yml ├── sqlfunc_test.go ├── column_test.go ├── drop.go ├── LICENSE ├── drop_test.go ├── delete_test.go ├── delete.go ├── update_test.go ├── testutils_test.go ├── sqlfunc.go ├── insert_test.go ├── literal.go ├── dialects ├── mysql.go ├── sqlite3.go └── postgresql.go ├── create_test.go ├── insert.go ├── sqlbuilder.go ├── update.go ├── literal_test.go ├── select_test.go ├── condition.go ├── sqlbuilder_test.go ├── create.go ├── alter_test.go ├── condition_test.go ├── table_test.go ├── alter.go ├── select.go ├── README.md ├── table.go └── column.go /integration_test/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | package for integration test(go-sqlbuilder) 3 | */ 4 | package sqlbuilder_integration 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | - tip 6 | 7 | addons: 8 | postgresql: "9.3" 9 | 10 | before_install: 11 | - go get github.com/axw/gocov/gocov 12 | - go get github.com/mattn/goveralls 13 | - go get golang.org/x/tools/cmd/cover 14 | 15 | before_script: 16 | - mysql -e 'create database go_sqlbuilder_test1;' 17 | - mysql -e 'create database go_sqlbuilder_test2;' 18 | - psql -c 'create database go_sqlbuilder_test' -U postgres 19 | 20 | script: 21 | - $HOME/gopath/bin/goveralls -repotoken IgwKyLgyhaPzKKNdryx4T3swQIiqfO1Rb 22 | - go test -v ./integration_test 23 | -------------------------------------------------------------------------------- /sqlfunc_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSqlFuncImplements(t *testing.T) { 9 | fnImplColumn := func(i interface{}) bool { 10 | return reflect.TypeOf(i).Implements(reflect.TypeOf(new(Column)).Elem()) 11 | } 12 | fnImplColumn(&columnImpl{}) 13 | } 14 | 15 | func TestSqlFunc(t *testing.T) { 16 | b := newBuilder() 17 | table1 := NewTable( 18 | "TABLE_A", 19 | &TableOption{}, 20 | IntColumn("id", &ColumnOption{ 21 | PrimaryKey: true, 22 | }), 23 | IntColumn("test1", nil), 24 | IntColumn("test2", nil), 25 | ) 26 | 27 | Func("funcname", table1.C("id")).serialize(b) 28 | if `funcname("TABLE_A"."id")` != b.query.String() { 29 | t.Errorf("failed") 30 | } 31 | if len(b.Args()) != 0 { 32 | t.Errorf("failed") 33 | } 34 | if b.Err() != nil { 35 | t.Errorf("failed") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /column_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestColumnImplements(t *testing.T) { 9 | fnImplColumn := func(i interface{}) bool { 10 | return reflect.TypeOf(i).Implements(reflect.TypeOf(new(Column)).Elem()) 11 | } 12 | if !fnImplColumn(&columnImpl{}) { 13 | t.Errorf("fail") 14 | } 15 | if !fnImplColumn(&errorColumn{}) { 16 | t.Errorf("fail") 17 | } 18 | if !fnImplColumn(&aliasColumn{}) { 19 | t.Errorf("fail") 20 | } 21 | } 22 | 23 | func TestColumnOptionImpl(t *testing.T) { 24 | if !reflect.DeepEqual(&columnConfigImpl{ 25 | name: "name", 26 | typ: ColumnTypeBytes, 27 | opt: &ColumnOption{ 28 | Unique: true, 29 | }}, newColumnConfigImpl("name", ColumnTypeBytes, &ColumnOption{Unique: true})) { 30 | t.Errorf("fail") 31 | } 32 | if !reflect.DeepEqual(&columnConfigImpl{ 33 | name: "name", 34 | typ: ColumnTypeBytes, 35 | opt: &ColumnOption{}, 36 | }, newColumnConfigImpl("name", ColumnTypeBytes, nil)) { 37 | t.Errorf("fail") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /drop.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // DeleteTableStatement represents a "DROP TABLE" statement. 4 | type DropTableStatement struct { 5 | table Table 6 | 7 | err error 8 | } 9 | 10 | // DropTable returns new "DROP TABLE" statement. The table is Table object to drop. 11 | func DropTable(tbl Table) *DropTableStatement { 12 | if tbl == nil { 13 | return &DropTableStatement{ 14 | err: newError("table is nil."), 15 | } 16 | } 17 | if _, ok := tbl.(*table); !ok { 18 | return &DropTableStatement{ 19 | err: newError("table is not natural table."), 20 | } 21 | } 22 | return &DropTableStatement{ 23 | table: tbl, 24 | } 25 | } 26 | 27 | // ToSql generates query string, placeholder arguments, and returns err on errors. 28 | func (b *DropTableStatement) ToSql() (query string, args []interface{}, err error) { 29 | bldr := newBuilder() 30 | defer func() { 31 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 32 | }() 33 | if b.err != nil { 34 | bldr.SetError(b.err) 35 | return 36 | } 37 | 38 | bldr.Append("DROP TABLE ") 39 | bldr.AppendItem(b.table) 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 umisama 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /drop_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDropTable(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | }), 14 | IntColumn("test1", nil), 15 | IntColumn("test2", nil), 16 | ) 17 | table2 := NewTable( 18 | "TABLE_B", 19 | &TableOption{}, 20 | IntColumn("id", &ColumnOption{ 21 | PrimaryKey: true, 22 | }), 23 | ) 24 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 25 | 26 | var cases = []statementTestCase{{ 27 | stmt: DropTable(table1), 28 | query: `DROP TABLE "TABLE_A";`, 29 | args: []interface{}{}, 30 | errmsg: "", 31 | }, { 32 | stmt: DropTable(nil), 33 | query: ``, 34 | args: []interface{}{}, 35 | errmsg: "sqlbuilder: table is nil.", 36 | }, { 37 | stmt: DropTable(tableJoined), 38 | query: ``, 39 | args: []interface{}{}, 40 | errmsg: "sqlbuilder: table is not natural table.", 41 | }} 42 | for num, c := range cases { 43 | mes, args, ok := c.Run() 44 | if !ok { 45 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /delete_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDelete(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | }), 14 | IntColumn("test1", nil), 15 | IntColumn("test2", nil), 16 | ) 17 | table2 := NewTable( 18 | "TABLE_B", 19 | &TableOption{}, 20 | IntColumn("id", &ColumnOption{ 21 | PrimaryKey: true, 22 | }), 23 | ) 24 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 25 | 26 | var cases = []statementTestCase{{ 27 | stmt: Delete(table1).Where(table1.C("id").Eq(1)), 28 | query: `DELETE FROM "TABLE_A" WHERE "TABLE_A"."id"=?;`, 29 | args: []interface{}{int64(1)}, 30 | errmsg: "", 31 | }, { 32 | stmt: Delete(nil).Where(table1.C("id").Eq(1)), 33 | query: ``, 34 | args: []interface{}{}, 35 | errmsg: "sqlbuilder: from is nil.", 36 | }, { 37 | stmt: Delete(tableJoined).Where(table1.C("id").Eq(1)), 38 | query: ``, 39 | args: []interface{}{}, 40 | errmsg: "sqlbuilder: CreateTable can use only natural table.", 41 | }} 42 | 43 | for num, c := range cases { 44 | mes, args, ok := c.Run() 45 | if !ok { 46 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /delete.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // DeleteStatement represents a DELETE statement. 4 | type DeleteStatement struct { 5 | from Table 6 | where Condition 7 | 8 | err error 9 | } 10 | 11 | // Delete returns new DELETE statement. The table is Table object to delete from. 12 | func Delete(from Table) *DeleteStatement { 13 | if from == nil { 14 | return &DeleteStatement{ 15 | err: newError("from is nil."), 16 | } 17 | } 18 | if _, ok := from.(*table); !ok { 19 | return &DeleteStatement{ 20 | err: newError("CreateTable can use only natural table."), 21 | } 22 | } 23 | return &DeleteStatement{ 24 | from: from, 25 | } 26 | } 27 | 28 | // Where sets WHERE clause. cond is filter condition. 29 | func (b *DeleteStatement) Where(cond Condition) *DeleteStatement { 30 | if b.err != nil { 31 | return b 32 | } 33 | for _, col := range cond.columns() { 34 | if !b.from.hasColumn(col) { 35 | b.err = newError("column not found in FROM") 36 | return b 37 | } 38 | } 39 | b.where = cond 40 | return b 41 | } 42 | 43 | // ToSql generates query string, placeholder arguments, and returns err on errors. 44 | func (b *DeleteStatement) ToSql() (query string, args []interface{}, err error) { 45 | bldr := newBuilder() 46 | defer func() { 47 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 48 | }() 49 | if b.err != nil { 50 | bldr.SetError(b.err) 51 | return 52 | } 53 | 54 | bldr.Append("DELETE FROM ") 55 | bldr.AppendItem(b.from) 56 | 57 | if b.where != nil { 58 | bldr.Append(" WHERE ") 59 | bldr.AppendItem(b.where) 60 | } 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /update_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUpdate(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | }), 14 | IntColumn("test1", nil), 15 | IntColumn("test2", nil), 16 | ) 17 | table2 := NewTable( 18 | "TABLE_B", 19 | &TableOption{}, 20 | IntColumn("id", &ColumnOption{ 21 | PrimaryKey: true, 22 | }), 23 | ) 24 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 25 | 26 | var cases = []statementTestCase{{ 27 | stmt: Update(table1).Where(table1.C("id").Eq(1)). 28 | Set(table1.C("test1"), 10). 29 | Set(table1.C("test2"), 20). 30 | OrderBy(true, table1.C("test1")). 31 | Limit(1). 32 | Offset(2), 33 | query: `UPDATE "TABLE_A" SET "test1"=?, "test2"=? WHERE "TABLE_A"."id"=? ORDER BY "TABLE_A"."test1" DESC LIMIT ? OFFSET ?;`, 34 | args: []interface{}{int64(10), int64(20), int64(1), 1, 2}, 35 | errmsg: "", 36 | }, { 37 | stmt: Update(table1).Where(table1.C("id").Eq(1)). 38 | Set(table1.C("test1"), 10). 39 | Set(table1.C("test2"), 20), 40 | query: `UPDATE "TABLE_A" SET "test1"=?, "test2"=? WHERE "TABLE_A"."id"=?;`, 41 | args: []interface{}{int64(10), int64(20), int64(1)}, 42 | errmsg: "", 43 | }, { 44 | stmt: Update(nil).Where(table1.C("id").Eq(1)). 45 | Set(table1.C("test1"), 10). 46 | Set(table1.C("test2"), 20), 47 | query: ``, 48 | args: []interface{}{}, 49 | errmsg: "sqlbuilder: table is nil.", 50 | }, { 51 | stmt: Update(table1).Where(table1.C("id").Eq(1)), 52 | query: ``, 53 | args: []interface{}{}, 54 | errmsg: "sqlbuilder: length of sets is 0.", 55 | }, { 56 | stmt: Update(table1).Where(table1.C("id").Eq(1)). 57 | Set(table1.C("test1"), "foo"), 58 | query: ``, 59 | args: []interface{}{}, 60 | errmsg: "sqlbuilder: int column not accept string.", 61 | }, { 62 | stmt: Update(tableJoined), 63 | query: ``, 64 | args: []interface{}{}, 65 | errmsg: "sqlbuilder: length of sets is 0.", 66 | }} 67 | for num, c := range cases { 68 | mes, args, ok := c.Run() 69 | if !ok { 70 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /testutils_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | type statementTestCase struct { 8 | stmt Statement 9 | query string 10 | args []interface{} 11 | errmsg string 12 | } 13 | 14 | func (testCase statementTestCase) Run() (message string, args []interface{}, ok bool) { 15 | query, args, err := testCase.stmt.ToSql() 16 | if len(testCase.errmsg) != 0 { 17 | if err == nil { 18 | return "error: expect returns error but got nil.", []interface{}{}, false 19 | } 20 | if err.Error() != testCase.errmsg { 21 | return "error: expect error message is '%s' but got '%s'.", []interface{}{err.Error(), testCase.errmsg}, false 22 | } 23 | } else { 24 | if err != nil { 25 | return "error: expect returns no error got %s.", []interface{}{err.Error()}, false 26 | } 27 | } 28 | if testCase.query != query { 29 | return "expect returns query \n%s \nbut got\n%s.", []interface{}{testCase.query, query}, false 30 | } 31 | if !reflect.DeepEqual(testCase.args, args) { 32 | return "expect returns arguments \n%s \nbut got\n%s.", []interface{}{testCase.args, args}, false 33 | } 34 | return "", nil, true 35 | } 36 | 37 | type conditionTestCase struct { 38 | cond Condition 39 | query string 40 | args []interface{} 41 | errmsg string 42 | } 43 | 44 | func (testCase conditionTestCase) Run() (message string, args []interface{}, ok bool) { 45 | bldr := newBuilder() 46 | testCase.cond.serialize(bldr) 47 | if len(testCase.errmsg) != 0 { 48 | if bldr.err == nil { 49 | return "error: expect returns error but got nil.", []interface{}{}, false 50 | } 51 | if bldr.err.Error() != testCase.errmsg { 52 | return "error: expect error message is '%s' but got '%s'.", []interface{}{bldr.err.Error(), testCase.errmsg}, false 53 | } 54 | } else { 55 | if bldr.err != nil { 56 | return "error: expect returns no error got %s.", []interface{}{bldr.err.Error()}, false 57 | } 58 | } 59 | if bldr.query.String() != testCase.query { 60 | return "expect returns query \n%s \nbut got\n%s.", []interface{}{testCase.query, bldr.query.String()}, false 61 | } 62 | if !reflect.DeepEqual(bldr.args, testCase.args) { 63 | return "expect returns arguments \n%s \nbut got\n%s.", []interface{}{testCase.args, args}, false 64 | } 65 | return "", nil, true 66 | } 67 | -------------------------------------------------------------------------------- /sqlfunc.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // SqlFunc represents function on SQL(ex:count(*)). This can be use in the same way as Column. 4 | type SqlFunc interface { 5 | Column 6 | 7 | columns() []Column 8 | } 9 | 10 | type sqlFuncColumnList []Column 11 | 12 | func (l sqlFuncColumnList) serialize(bldr *builder) { 13 | first := true 14 | for _, part := range l { 15 | if first { 16 | first = false 17 | } else { 18 | bldr.Append(" , ") 19 | } 20 | part.serialize(bldr) 21 | } 22 | } 23 | 24 | type sqlFuncImpl struct { 25 | name string 26 | args sqlFuncColumnList 27 | } 28 | 29 | // Func returns new SQL function. The name is function name, and the args is arguments of function 30 | func Func(name string, args ...Column) SqlFunc { 31 | return &sqlFuncImpl{ 32 | name: name, 33 | args: args, 34 | } 35 | } 36 | 37 | func (m *sqlFuncImpl) As(alias string) Column { 38 | return &aliasColumn{ 39 | column: m, 40 | alias: alias, 41 | } 42 | } 43 | 44 | func (m *sqlFuncImpl) column_name() string { 45 | return m.name 46 | } 47 | 48 | func (m *sqlFuncImpl) not_null() bool { 49 | return true 50 | } 51 | 52 | func (m *sqlFuncImpl) config() ColumnConfig { 53 | return nil 54 | } 55 | 56 | func (m *sqlFuncImpl) acceptType(interface{}) bool { 57 | return false 58 | } 59 | 60 | func (m *sqlFuncImpl) serialize(bldr *builder) { 61 | bldr.Append(m.name) 62 | bldr.Append("(") 63 | bldr.AppendItem(m.args) 64 | bldr.Append(")") 65 | } 66 | 67 | func (left *sqlFuncImpl) Eq(right interface{}) Condition { 68 | return newBinaryOperationCondition(left, right, "=") 69 | } 70 | 71 | func (left *sqlFuncImpl) NotEq(right interface{}) Condition { 72 | return newBinaryOperationCondition(left, right, "<>") 73 | } 74 | 75 | func (left *sqlFuncImpl) Gt(right interface{}) Condition { 76 | return newBinaryOperationCondition(left, right, ">") 77 | } 78 | 79 | func (left *sqlFuncImpl) GtEq(right interface{}) Condition { 80 | return newBinaryOperationCondition(left, right, ">=") 81 | } 82 | 83 | func (left *sqlFuncImpl) Lt(right interface{}) Condition { 84 | return newBinaryOperationCondition(left, right, "<") 85 | } 86 | 87 | func (left *sqlFuncImpl) LtEq(right interface{}) Condition { 88 | return newBinaryOperationCondition(left, right, "<=") 89 | } 90 | 91 | func (left *sqlFuncImpl) Like(right string) Condition { 92 | return newBinaryOperationCondition(left, right, " LIKE ") 93 | } 94 | 95 | func (left *sqlFuncImpl) Between(lower, higher interface{}) Condition { 96 | return newBetweenCondition(left, lower, higher) 97 | } 98 | 99 | func (left *sqlFuncImpl) In(vals ...interface{}) Condition { 100 | return newInCondition(left, vals...) 101 | } 102 | 103 | func (m *sqlFuncImpl) columns() []Column { 104 | return m.args 105 | } 106 | -------------------------------------------------------------------------------- /insert_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestInsert(t *testing.T) { 9 | table1 := NewTable( 10 | "TABLE_A", 11 | &TableOption{}, 12 | IntColumn("id", &ColumnOption{ 13 | PrimaryKey: true, 14 | }), 15 | StringColumn("str", &ColumnOption{ 16 | Size: 255, 17 | }), 18 | BoolColumn("bool", nil), 19 | FloatColumn("float", nil), 20 | DateColumn("date", nil), 21 | BytesColumn("bytes", nil), 22 | ) 23 | table2 := NewTable( 24 | "TABLE_B", 25 | &TableOption{}, 26 | IntColumn("id", &ColumnOption{ 27 | PrimaryKey: true, 28 | }), 29 | ) 30 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 31 | 32 | var cases = []statementTestCase{{ 33 | stmt: Insert(table1). 34 | Columns(table1.C("str"), table1.C("bool"), table1.C("float"), table1.C("date"), table1.C("bytes")). 35 | Values("hoge", true, 0.1, time.Unix(0, 0).UTC(), []byte{0x01}), 36 | query: `INSERT INTO "TABLE_A" ( "str", "bool", "float", "date", "bytes" ) VALUES ( ?, ?, ?, ?, ? );`, 37 | args: []interface{}{"hoge", true, 0.1, time.Unix(0, 0).UTC(), []byte{0x01}}, 38 | errmsg: "", 39 | }, { 40 | stmt: Insert(table1). 41 | Set(table1.C("str"), "hoge"). 42 | Set(table1.C("bool"), true). 43 | Set(table1.C("float"), 0.1). 44 | Set(table1.C("date"), time.Unix(0, 0).UTC()). 45 | Set(table1.C("bytes"), []byte{0x01}), 46 | query: `INSERT INTO "TABLE_A" ( "str", "bool", "float", "date", "bytes" ) VALUES ( ?, ?, ?, ?, ? );`, 47 | args: []interface{}{"hoge", true, 0.1, time.Unix(0, 0).UTC(), []byte{0x01}}, 48 | errmsg: "", 49 | }, { 50 | stmt: Insert(table1).Values(1, "hoge", true, 0.1, time.Unix(0, 0).UTC(), []byte{0x01}), 51 | query: `INSERT INTO "TABLE_A" ( "id", "str", "bool", "float", "date", "bytes" ) VALUES ( ?, ?, ?, ?, ?, ? );`, 52 | args: []interface{}{int64(1), "hoge", true, 0.1, time.Unix(0, 0).UTC(), []byte{0x01}}, 53 | errmsg: "", 54 | }, { 55 | stmt: Insert(table1).Columns(table1.C("id")).Values(1, 2, 3), 56 | query: "", 57 | args: []interface{}{}, 58 | errmsg: "sqlbuilder: 1 values needed, but got 3.", 59 | }, { 60 | stmt: Insert(nil).Columns(table1.C("id")).Values(1), 61 | query: "", 62 | args: []interface{}{}, 63 | errmsg: "sqlbuilder: table is nil.", 64 | }, { 65 | stmt: Insert(table1).Columns(table1.C("str")).Values(1), 66 | query: "", 67 | args: []interface{}{}, 68 | errmsg: "sqlbuilder: string column not accept int.", 69 | }, { 70 | stmt: Insert(tableJoined).Columns(table1.C("str")).Values(1), 71 | query: "", 72 | args: []interface{}{}, 73 | errmsg: "sqlbuilder: table is not natural table.", 74 | }} 75 | 76 | for num, c := range cases { 77 | mes, args, ok := c.Run() 78 | if !ok { 79 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /literal.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | sqldriver "database/sql/driver" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | type literal interface { 12 | serializable 13 | Raw() interface{} 14 | IsNil() bool 15 | } 16 | 17 | type literalImpl struct { 18 | raw interface{} 19 | placeholder bool 20 | } 21 | 22 | func toLiteral(v interface{}) literal { 23 | refv := reflect.ValueOf(v) 24 | if v != nil && 25 | refv.Kind() == reflect.Ptr && 26 | !refv.Type().Implements(reflect.TypeOf((*sqldriver.Valuer)(nil)).Elem()) { 27 | if refv.IsNil() { 28 | v = nil 29 | } else { 30 | v = reflect.Indirect(refv).Interface() 31 | } 32 | } 33 | return &literalImpl{ 34 | raw: v, 35 | placeholder: true, 36 | } 37 | } 38 | 39 | func (l *literalImpl) serialize(bldr *builder) { 40 | val, err := l.converted() 41 | if err != nil { 42 | bldr.SetError(err) 43 | return 44 | } 45 | 46 | if l.placeholder { 47 | bldr.AppendValue(val) 48 | } else { 49 | bldr.Append(l.string()) 50 | } 51 | return 52 | } 53 | 54 | func (l *literalImpl) IsNil() bool { 55 | if l.raw == nil { 56 | return true 57 | } 58 | 59 | v := reflect.ValueOf(l.raw) 60 | switch v.Kind() { 61 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 62 | return v.IsNil() 63 | default: 64 | return false 65 | } 66 | } 67 | 68 | // convert to sqldriver.Value(int64/float64/bool/[]byte/string/time.Time) 69 | func (l *literalImpl) converted() (interface{}, error) { 70 | switch t := l.raw.(type) { 71 | case int, int8, int16, int32, int64: 72 | return int64(reflect.ValueOf(t).Int()), nil 73 | case uint, uint8, uint16, uint32, uint64: 74 | return int64(reflect.ValueOf(t).Uint()), nil 75 | case float32, float64: 76 | return reflect.ValueOf(l.raw).Float(), nil 77 | case bool: 78 | return t, nil 79 | case []byte: 80 | return t, nil 81 | case string: 82 | return t, nil 83 | case time.Time: 84 | return t, nil 85 | case sqldriver.Valuer: 86 | return t, nil 87 | case nil: 88 | return nil, nil 89 | default: 90 | return nil, newError("got %T type, but literal is not supporting this.", t) 91 | } 92 | } 93 | 94 | func (l *literalImpl) string() string { 95 | val, err := l.converted() 96 | if err != nil { 97 | return "" 98 | } 99 | 100 | switch t := val.(type) { 101 | case int64: 102 | return strconv.FormatInt(t, 10) 103 | case float64: 104 | return strconv.FormatFloat(t, 'f', 10, 64) 105 | case bool: 106 | return strconv.FormatBool(t) 107 | case string: 108 | return t 109 | case []byte: 110 | return string(t) 111 | case time.Time: 112 | return t.Format("2006-01-02 15:04:05") 113 | case fmt.Stringer: 114 | return t.String() 115 | case nil: 116 | return "NULL" 117 | default: 118 | return "" 119 | } 120 | } 121 | 122 | func (l *literalImpl) Raw() interface{} { 123 | return l.raw 124 | } 125 | -------------------------------------------------------------------------------- /dialects/mysql.go: -------------------------------------------------------------------------------- 1 | package dialects 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | sb "github.com/umisama/go-sqlbuilder" 7 | "time" 8 | ) 9 | 10 | type MySql struct{} 11 | 12 | func (m MySql) QuerySuffix() string { 13 | return ";" 14 | } 15 | 16 | func (m MySql) BindVar(i int) string { 17 | return "?" 18 | } 19 | 20 | func (m MySql) QuoteField(field interface{}) string { 21 | str := "" 22 | bracket := true 23 | switch t := field.(type) { 24 | case string: 25 | str = t 26 | case []byte: 27 | str = string(t) 28 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 29 | str = fmt.Sprint(field) 30 | case float32, float64: 31 | str = fmt.Sprint(field) 32 | case time.Time: 33 | str = t.Format("2006-01-02 15:04:05") 34 | case bool: 35 | if t { 36 | str = "TRUE" 37 | } else { 38 | str = "FALSE" 39 | } 40 | bracket = false 41 | case nil: 42 | return "NULL" 43 | bracket = false 44 | } 45 | if bracket { 46 | str = "`" + str + "`" 47 | } 48 | return str 49 | } 50 | 51 | func (m MySql) ColumnTypeToString(cc sb.ColumnConfig) (string, error) { 52 | if cc.Option().SqlType != "" { 53 | return cc.Option().SqlType, nil 54 | } 55 | 56 | typ := "" 57 | switch cc.Type() { 58 | case sb.ColumnTypeInt: 59 | typ = "INTEGER" 60 | case sb.ColumnTypeString: 61 | typ = fmt.Sprintf("VARCHAR(%d)", cc.Option().Size) 62 | case sb.ColumnTypeDate: 63 | typ = "DATETIME" 64 | case sb.ColumnTypeFloat: 65 | typ = "FLOAT" 66 | case sb.ColumnTypeBool: 67 | typ = "BOOLEAN" 68 | case sb.ColumnTypeBytes: 69 | typ = "BLOB" 70 | } 71 | 72 | if typ == "" { 73 | return "", errors.New("dialects: unknown column type") 74 | } else { 75 | return typ, nil 76 | } 77 | } 78 | 79 | func (m MySql) ColumnOptionToString(co *sb.ColumnOption) (string, error) { 80 | opt := "" 81 | if co.PrimaryKey { 82 | opt = str_append(opt, "PRIMARY KEY") 83 | } 84 | if co.AutoIncrement { 85 | opt = str_append(opt, "AUTO_INCREMENT") 86 | } 87 | if co.NotNull { 88 | opt = str_append(opt, "NOT NULL") 89 | } 90 | if co.Unique { 91 | opt = str_append(opt, "UNIQUE") 92 | } 93 | 94 | return opt, nil 95 | } 96 | 97 | func (m MySql) TableOptionToString(to *sb.TableOption) (string, error) { 98 | opt := "" 99 | if to.Unique != nil { 100 | opt = str_append(opt, m.tableOptionUnique(to.Unique)) 101 | } 102 | 103 | return "", nil 104 | } 105 | 106 | func (m MySql) tableOptionUnique(op [][]string) string { 107 | opt := "" 108 | first_op := true 109 | for _, unique := range op { 110 | if first_op { 111 | first_op = false 112 | } else { 113 | opt += " " 114 | } 115 | 116 | opt += "UNIQUE(" 117 | first := true 118 | for _, col := range unique { 119 | if first { 120 | first = false 121 | } else { 122 | opt += ", " 123 | } 124 | opt += m.QuoteField(col) 125 | } 126 | opt += ")" 127 | } 128 | return opt 129 | } 130 | 131 | func str_append(str, opt string) string { 132 | if len(str) != 0 { 133 | str += " " 134 | } 135 | str += opt 136 | return str 137 | } 138 | -------------------------------------------------------------------------------- /dialects/sqlite3.go: -------------------------------------------------------------------------------- 1 | package dialects 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | sb "github.com/umisama/go-sqlbuilder" 7 | "time" 8 | ) 9 | 10 | type Sqlite struct{} 11 | 12 | func (m Sqlite) QuerySuffix() string { 13 | return ";" 14 | } 15 | 16 | func (m Sqlite) BindVar(i int) string { 17 | return "?" 18 | } 19 | 20 | func (m Sqlite) QuoteField(field interface{}) string { 21 | str := "" 22 | bracket := true 23 | switch t := field.(type) { 24 | case string: 25 | str = t 26 | case []byte: 27 | str = string(t) 28 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 29 | str = fmt.Sprint(field) 30 | case float32, float64: 31 | str = fmt.Sprint(field) 32 | case time.Time: 33 | str = t.Format("2006-01-02 15:04:05") 34 | case bool: 35 | if t { 36 | str = "TRUE" 37 | } else { 38 | str = "FALSE" 39 | } 40 | bracket = false 41 | case nil: 42 | return "NULL" 43 | bracket = false 44 | } 45 | if bracket { 46 | str = "\"" + str + "\"" 47 | } 48 | return str 49 | } 50 | 51 | func (m Sqlite) ColumnTypeToString(cc sb.ColumnConfig) (string, error) { 52 | if cc.Option().SqlType != "" { 53 | return cc.Option().SqlType, nil 54 | } 55 | 56 | typ := "" 57 | switch cc.Type() { 58 | case sb.ColumnTypeInt: 59 | typ = "INTEGER" 60 | case sb.ColumnTypeString: 61 | typ = "TEXT" 62 | case sb.ColumnTypeDate: 63 | typ = "DATE" 64 | case sb.ColumnTypeFloat: 65 | typ = "REAL" 66 | case sb.ColumnTypeBool: 67 | typ = "BOOLEAN" 68 | case sb.ColumnTypeBytes: 69 | typ = "BLOB" 70 | } 71 | if typ == "" { 72 | return "", errors.New("dialects: unknown column type") 73 | } else { 74 | return typ, nil 75 | } 76 | } 77 | 78 | func (m Sqlite) ColumnOptionToString(co *sb.ColumnOption) (string, error) { 79 | opt := "" 80 | if co.PrimaryKey { 81 | opt = str_append(opt, "PRIMARY KEY") 82 | } 83 | if co.AutoIncrement { 84 | opt = str_append(opt, "AUTOINCREMENT") 85 | } 86 | if co.NotNull { 87 | opt = str_append(opt, "NOT NULL") 88 | } 89 | if co.Unique { 90 | opt = str_append(opt, "UNIQUE") 91 | } 92 | if co.Default == nil { 93 | if !co.PrimaryKey { 94 | opt = str_append(opt, "DEFAULT NULL") 95 | } 96 | } else { 97 | opt = str_append(opt, "DEFAULT "+m.QuoteField(co.Default)) 98 | } 99 | 100 | return opt, nil 101 | } 102 | 103 | func (m Sqlite) TableOptionToString(to *sb.TableOption) (string, error) { 104 | opt := "" 105 | if to.Unique != nil { 106 | opt = str_append(opt, m.tableOptionUnique(to.Unique)) 107 | } 108 | 109 | return "", nil 110 | } 111 | 112 | func (m Sqlite) tableOptionUnique(op [][]string) string { 113 | opt := "" 114 | first_op := true 115 | for _, unique := range op { 116 | if first_op { 117 | first_op = false 118 | } else { 119 | opt += " " 120 | } 121 | 122 | opt += "UNIQUE(" 123 | first := true 124 | for _, col := range unique { 125 | if first { 126 | first = false 127 | } else { 128 | opt += ", " 129 | } 130 | opt += m.QuoteField(col) 131 | } 132 | opt += ")" 133 | } 134 | return opt 135 | } 136 | -------------------------------------------------------------------------------- /create_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCreate(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | AutoIncrement: true, 14 | }), 15 | IntColumn("test1", &ColumnOption{ 16 | Unique: true, 17 | }), 18 | StringColumn("test2", &ColumnOption{ 19 | Size: 255, 20 | }), 21 | ) 22 | table2 := NewTable( 23 | "TABLE_B", 24 | &TableOption{}, 25 | StringColumn("id", &ColumnOption{ 26 | PrimaryKey: true, 27 | AutoIncrement: true, 28 | SqlType: "VARCHAR(255)", 29 | }), 30 | AnyColumn("test1", &ColumnOption{ 31 | Unique: true, 32 | SqlType: "INTEGER", 33 | }), 34 | ) 35 | table3 := NewTable( 36 | "TABLE_C", 37 | &TableOption{ 38 | Unique: [][]string{{"test1", "test2"}}, 39 | }, 40 | IntColumn("id", &ColumnOption{ 41 | PrimaryKey: true, 42 | AutoIncrement: true, 43 | }), 44 | IntColumn("test1", &ColumnOption{ 45 | Unique: true, 46 | }), 47 | StringColumn("test2", &ColumnOption{ 48 | Size: 255, 49 | }), 50 | ) 51 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 52 | tableZeroColumns := &table{ 53 | name: "ZERO_TABLE", 54 | columns: make([]Column, 0), 55 | } 56 | 57 | var cases = []statementTestCase{{ 58 | stmt: CreateTable(table1).IfNotExists(), 59 | query: `CREATE TABLE IF NOT EXISTS "TABLE_A" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "test1" INTEGER UNIQUE, "test2" TEXT );`, 60 | args: []interface{}{}, 61 | errmsg: "", 62 | }, { 63 | stmt: CreateTable(table2).IfNotExists(), 64 | query: `CREATE TABLE IF NOT EXISTS "TABLE_B" ( "id" VARCHAR(255) PRIMARY KEY AUTOINCREMENT, "test1" INTEGER UNIQUE );`, 65 | args: []interface{}{}, 66 | errmsg: "", 67 | }, { 68 | stmt: CreateTable(table3).IfNotExists(), 69 | query: `CREATE TABLE IF NOT EXISTS "TABLE_C" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "test1" INTEGER UNIQUE, "test2" TEXT ) UNIQUE("test1", "test2");`, 70 | args: []interface{}{}, 71 | errmsg: "", 72 | }, { 73 | stmt: CreateIndex(table1).Name("I_TABLE_A").IfNotExists().Columns(table1.C("test1"), table1.C("test2")), 74 | query: `CREATE INDEX IF NOT EXISTS "I_TABLE_A" ON "TABLE_A" ( "test1", "test2" );`, 75 | args: []interface{}{}, 76 | errmsg: "", 77 | }, { 78 | stmt: CreateTable(tableZeroColumns), 79 | query: ``, 80 | args: []interface{}{}, 81 | errmsg: "sqlbuilder: CreateTableStatement needs one or more columns.", 82 | }, { 83 | stmt: CreateTable(nil), 84 | query: ``, 85 | args: []interface{}{}, 86 | errmsg: "sqlbuilder: table is nil.", 87 | }, { 88 | stmt: CreateTable(tableJoined), 89 | query: ``, 90 | args: []interface{}{}, 91 | errmsg: "sqlbuilder: CreateTable can use only natural table.", 92 | }, { 93 | stmt: CreateIndex(table1).Columns(table1.C("test1"), table1.C("test2")), 94 | query: ``, 95 | args: []interface{}{}, 96 | errmsg: "sqlbuilder: name was not setted.", 97 | }, { 98 | stmt: CreateIndex(table1).Name("I_TABLE_A"), 99 | query: ``, 100 | args: []interface{}{}, 101 | errmsg: "sqlbuilder: columns was not setted.", 102 | }} 103 | 104 | for num, c := range cases { 105 | mes, args, ok := c.Run() 106 | if !ok { 107 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /insert.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // InsertStatement represents a INSERT statement. 4 | type InsertStatement struct { 5 | columns ColumnList 6 | values []literal 7 | into Table 8 | 9 | err error 10 | } 11 | 12 | // Insert returns new INSERT statement. The table is Table object for into. 13 | func Insert(into Table) *InsertStatement { 14 | if into == nil { 15 | return &InsertStatement{ 16 | err: newError("table is nil."), 17 | } 18 | } 19 | if _, ok := into.(*table); !ok { 20 | return &InsertStatement{ 21 | err: newError("table is not natural table."), 22 | } 23 | } 24 | return &InsertStatement{ 25 | into: into, 26 | columns: make(ColumnList, 0), 27 | values: make([]literal, 0), 28 | } 29 | } 30 | 31 | // Columns sets columns for insert. This overwrite old results of Columns() or Set(). 32 | // If not set this, get error on ToSql(). 33 | func (b *InsertStatement) Columns(columns ...Column) *InsertStatement { 34 | if b.err != nil { 35 | return b 36 | } 37 | for _, col := range columns { 38 | if !b.into.hasColumn(col) { 39 | b.err = newError("column not found in table.") 40 | return b 41 | } 42 | } 43 | b.columns = ColumnList(columns) 44 | return b 45 | } 46 | 47 | // Values sets VALUES clause. This overwrite old results of Values() or Set(). 48 | func (b *InsertStatement) Values(values ...interface{}) *InsertStatement { 49 | if b.err != nil { 50 | return b 51 | } 52 | sl := make([]literal, len(values)) 53 | for i := range values { 54 | sl[i] = toLiteral(values[i]) 55 | } 56 | b.values = sl 57 | return b 58 | } 59 | 60 | // Set sets the column and value togeter. 61 | // Set cannot be called with Columns() or Values() in a statement. 62 | func (b *InsertStatement) Set(column Column, value interface{}) *InsertStatement { 63 | if b.err != nil { 64 | return b 65 | } 66 | if !b.into.hasColumn(column) { 67 | b.err = newError("column not found in FROM.") 68 | return b 69 | } 70 | b.columns = append(b.columns, column) 71 | b.values = append(b.values, toLiteral(value)) 72 | return b 73 | } 74 | 75 | // ToSql generates query string, placeholder arguments, and returns err on errors. 76 | func (b *InsertStatement) ToSql() (query string, args []interface{}, err error) { 77 | bldr := newBuilder() 78 | defer func() { 79 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 80 | }() 81 | if b.err != nil { 82 | bldr.SetError(b.err) 83 | return 84 | } 85 | 86 | // INSERT 87 | bldr.Append("INSERT") 88 | 89 | // INTO Table 90 | bldr.Append(" INTO ") 91 | bldr.AppendItem(b.into) 92 | 93 | // (COLUMN) 94 | if len(b.columns) == 0 { 95 | b.columns = b.into.Columns() 96 | } 97 | bldr.Append(" ( ") 98 | bldr.AppendItem(b.columns) 99 | bldr.Append(" )") 100 | 101 | // VALUES 102 | if len(b.columns) != len(b.values) { 103 | bldr.SetError(newError("%d values needed, but got %d.", len(b.columns), len(b.values))) 104 | return 105 | } 106 | for i := range b.columns { 107 | if !b.columns[i].acceptType(b.values[i]) { 108 | bldr.SetError(newError("%s column not accept %T.", 109 | b.columns[i].config().Type().String(), 110 | b.values[i].Raw())) 111 | return 112 | } 113 | } 114 | bldr.Append(" VALUES ( ") 115 | values := make([]serializable, len(b.values)) 116 | for i := range values { 117 | values[i] = b.values[i] 118 | } 119 | bldr.AppendItems(values, ", ") 120 | bldr.Append(" )") 121 | 122 | return 123 | } 124 | -------------------------------------------------------------------------------- /sqlbuilder.go: -------------------------------------------------------------------------------- 1 | // Package sqlbuilder is a SQL-query builder for golang. This supports you using relational database with more readable and flexible code than raw SQL query string. 2 | // 3 | // See https://github.com/umisama/go-sqlbuilder for more infomation. 4 | package sqlbuilder 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | ) 10 | 11 | var _dialect Dialect = nil 12 | 13 | // Star reprecents * column. 14 | var Star Column = &columnImpl{nil, nil} 15 | 16 | // Statement reprecents a statement(SELECT/INSERT/UPDATE and other) 17 | type Statement interface { 18 | ToSql() (query string, attrs []interface{}, err error) 19 | } 20 | 21 | type serializable interface { 22 | serialize(b *builder) 23 | } 24 | 25 | // Dialect encapsulates behaviors that differ across SQL database. 26 | type Dialect interface { 27 | QuerySuffix() string 28 | BindVar(i int) string 29 | QuoteField(field interface{}) string 30 | ColumnTypeToString(ColumnConfig) (string, error) 31 | ColumnOptionToString(*ColumnOption) (string, error) 32 | TableOptionToString(*TableOption) (string, error) 33 | } 34 | 35 | // SetDialect sets dialect for SQL server. 36 | // Must set dialect at first. 37 | func SetDialect(opt Dialect) { 38 | _dialect = opt 39 | } 40 | 41 | func dialect() Dialect { 42 | if _dialect == nil { 43 | panic(newError("dialect is not setted. Call SetDialect() first.")) 44 | } 45 | return _dialect 46 | } 47 | 48 | type builder struct { 49 | query *bytes.Buffer 50 | args []interface{} 51 | err error 52 | } 53 | 54 | func newBuilder() *builder { 55 | return &builder{ 56 | query: bytes.NewBuffer(make([]byte, 0, 256)), 57 | args: make([]interface{}, 0, 8), 58 | err: nil, 59 | } 60 | } 61 | 62 | func (b *builder) Err() error { 63 | if b.err != nil { 64 | return b.err 65 | } 66 | return nil 67 | } 68 | 69 | func (b *builder) Query() string { 70 | if b.err != nil { 71 | return "" 72 | } 73 | return b.query.String() + dialect().QuerySuffix() 74 | } 75 | 76 | func (b *builder) Args() []interface{} { 77 | if b.err != nil { 78 | return []interface{}{} 79 | } 80 | return b.args 81 | } 82 | 83 | func (b *builder) SetError(err error) { 84 | if b.err != nil { 85 | return 86 | } 87 | b.err = err 88 | return 89 | } 90 | 91 | func (b *builder) Append(query string) { 92 | if b.err != nil { 93 | return 94 | } 95 | 96 | b.query.WriteString(query) 97 | } 98 | 99 | func (b *builder) AppendValue(val interface{}) { 100 | if b.err != nil { 101 | return 102 | } 103 | 104 | b.query.WriteString(dialect().BindVar(len(b.args) + 1)) 105 | b.args = append(b.args, val) 106 | return 107 | } 108 | 109 | func (b *builder) AppendItems(parts []serializable, sep string) { 110 | if parts == nil { 111 | return 112 | } 113 | 114 | first := true 115 | for _, part := range parts { 116 | if first { 117 | first = false 118 | } else { 119 | b.Append(sep) 120 | } 121 | part.serialize(b) 122 | } 123 | return 124 | } 125 | 126 | func (b *builder) AppendItem(part serializable) { 127 | if part == nil { 128 | return 129 | } 130 | part.serialize(b) 131 | } 132 | 133 | type errors struct { 134 | fmt string 135 | args []interface{} 136 | } 137 | 138 | func newError(fmt string, args ...interface{}) *errors { 139 | return &errors{ 140 | fmt: fmt, 141 | args: args, 142 | } 143 | } 144 | 145 | func (err *errors) Error() string { 146 | return fmt.Sprintf("sqlbuilder: "+err.fmt, err.args...) 147 | } 148 | -------------------------------------------------------------------------------- /dialects/postgresql.go: -------------------------------------------------------------------------------- 1 | package dialects 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | sb "github.com/umisama/go-sqlbuilder" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | type Postgresql struct{} 12 | 13 | func (m Postgresql) QuerySuffix() string { 14 | return ";" 15 | } 16 | 17 | func (m Postgresql) BindVar(i int) string { 18 | return "$" + strconv.Itoa(i) 19 | } 20 | 21 | func (m Postgresql) quoteField(field interface{}) (string, bool) { 22 | str := "" 23 | bracket := true 24 | switch t := field.(type) { 25 | case string: 26 | str = t 27 | case []byte: 28 | str = string(t) 29 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 30 | str = fmt.Sprint(field) 31 | case float32, float64: 32 | str = fmt.Sprint(field) 33 | case time.Time: 34 | str = t.Format("2006-01-02 15:04:05") 35 | case bool: 36 | if t { 37 | str = "TRUE" 38 | } else { 39 | str = "FALSE" 40 | } 41 | bracket = false 42 | case nil: 43 | str = "NULL" 44 | bracket = false 45 | } 46 | return str, bracket 47 | } 48 | 49 | func (m Postgresql) QuoteField(field interface{}) string { 50 | str, bracket := m.quoteField(field) 51 | if bracket { 52 | str = "\"" + str + "\"" 53 | } 54 | return str 55 | } 56 | 57 | func (m Postgresql) ColumnTypeToString(cc sb.ColumnConfig) (string, error) { 58 | if cc.Option().SqlType != "" { 59 | return cc.Option().SqlType, nil 60 | } 61 | 62 | typ := "" 63 | switch cc.Type() { 64 | case sb.ColumnTypeInt: 65 | if cc.Option().AutoIncrement { 66 | typ = "SERIAL" 67 | } else { 68 | typ = "BIGINT" 69 | } 70 | case sb.ColumnTypeString: 71 | typ = fmt.Sprintf("VARCHAR(%d)", cc.Option().Size) 72 | case sb.ColumnTypeDate: 73 | typ = "TIMESTAMP" 74 | case sb.ColumnTypeFloat: 75 | typ = "REAL" 76 | case sb.ColumnTypeBool: 77 | typ = "BOOLEAN" 78 | case sb.ColumnTypeBytes: 79 | typ = "BYTEA" 80 | } 81 | 82 | if typ == "" { 83 | return "", errors.New("dialects: unknown column type") 84 | } else { 85 | return typ, nil 86 | } 87 | } 88 | 89 | func (m Postgresql) ColumnOptionToString(co *sb.ColumnOption) (string, error) { 90 | opt := "" 91 | if co.PrimaryKey { 92 | opt = str_append(opt, "PRIMARY KEY") 93 | } 94 | if co.AutoIncrement { 95 | // do nothing 96 | } 97 | if co.NotNull { 98 | opt = str_append(opt, "NOT NULL") 99 | } 100 | if co.Unique { 101 | opt = str_append(opt, "UNIQUE") 102 | } 103 | if co.Default == nil { 104 | if !co.PrimaryKey { 105 | opt = str_append(opt, "DEFAULT NULL") 106 | } 107 | } else { 108 | str, bracket := m.quoteField(co.Default) 109 | if bracket { 110 | str = "'" + str + "'" 111 | } 112 | opt = str_append(opt, "DEFAULT "+str) 113 | } 114 | 115 | return opt, nil 116 | } 117 | 118 | func (m Postgresql) TableOptionToString(to *sb.TableOption) (string, error) { 119 | opt := "" 120 | if to.Unique != nil { 121 | opt = str_append(opt, m.tableOptionUnique(to.Unique)) 122 | } 123 | 124 | return "", nil 125 | } 126 | 127 | func (m Postgresql) tableOptionUnique(op [][]string) string { 128 | opt := "" 129 | first_op := true 130 | for _, unique := range op { 131 | if first_op { 132 | first_op = false 133 | } else { 134 | opt += " " 135 | } 136 | 137 | opt += "UNIQUE(" 138 | first := true 139 | for _, col := range unique { 140 | if first { 141 | first = false 142 | } else { 143 | opt += ", " 144 | } 145 | opt += m.QuoteField(col) 146 | } 147 | opt += ")" 148 | } 149 | return opt 150 | } 151 | -------------------------------------------------------------------------------- /update.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // UpdateStatement represents a UPDATE statement. 4 | type UpdateStatement struct { 5 | table Table 6 | set []serializable 7 | where Condition 8 | orderBy []serializable 9 | limit int 10 | offset int 11 | 12 | err error 13 | } 14 | 15 | // Update returns new UPDATE statement. The table is Table object to update. 16 | func Update(tbl Table) *UpdateStatement { 17 | if tbl == nil { 18 | return &UpdateStatement{ 19 | err: newError("table is nil."), 20 | } 21 | } 22 | return &UpdateStatement{ 23 | table: tbl, 24 | set: make([]serializable, 0), 25 | } 26 | } 27 | 28 | // Set sets SETS clause like col=val. Call many time for update multi columns. 29 | func (b *UpdateStatement) Set(col Column, val interface{}) *UpdateStatement { 30 | if b.err != nil { 31 | return b 32 | } 33 | if !b.table.hasColumn(col) { 34 | b.err = newError("column not found in FROM.") 35 | return b 36 | } 37 | b.set = append(b.set, newUpdateValue(col, val)) 38 | return b 39 | } 40 | 41 | // Where sets WHERE clause. The cond is filter condition. 42 | func (b *UpdateStatement) Where(cond Condition) *UpdateStatement { 43 | if b.err != nil { 44 | return b 45 | } 46 | b.where = cond 47 | return b 48 | } 49 | 50 | // Limit sets LIMIT clause. 51 | func (b *UpdateStatement) Limit(limit int) *UpdateStatement { 52 | if b.err != nil { 53 | return b 54 | } 55 | b.limit = limit 56 | return b 57 | } 58 | 59 | // Limit sets OFFSET clause. 60 | func (b *UpdateStatement) Offset(offset int) *UpdateStatement { 61 | if b.err != nil { 62 | return b 63 | } 64 | b.offset = offset 65 | return b 66 | } 67 | 68 | // OrderBy sets "ORDER BY" clause. Use descending order if the desc is true, by the columns. 69 | func (b *UpdateStatement) OrderBy(desc bool, columns ...Column) *UpdateStatement { 70 | if b.err != nil { 71 | return b 72 | } 73 | if b.orderBy == nil { 74 | b.orderBy = make([]serializable, 0) 75 | } 76 | 77 | for _, c := range columns { 78 | b.orderBy = append(b.orderBy, newOrderBy(desc, c)) 79 | } 80 | return b 81 | } 82 | 83 | // ToSql generates query string, placeholder arguments, and returns err on errors. 84 | func (b *UpdateStatement) ToSql() (query string, args []interface{}, err error) { 85 | bldr := newBuilder() 86 | defer func() { 87 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 88 | }() 89 | if b.err != nil { 90 | bldr.SetError(b.err) 91 | return 92 | } 93 | 94 | // UPDATE TABLE SET (COLUMN=VALUE) 95 | bldr.Append("UPDATE ") 96 | bldr.AppendItem(b.table) 97 | 98 | bldr.Append(" SET ") 99 | if len(b.set) != 0 { 100 | bldr.AppendItems(b.set, ", ") 101 | } else { 102 | bldr.SetError(newError("length of sets is 0.")) 103 | } 104 | 105 | // WHERE 106 | if b.where != nil { 107 | bldr.Append(" WHERE ") 108 | bldr.AppendItem(b.where) 109 | } 110 | 111 | // ORDER BY 112 | if b.orderBy != nil { 113 | bldr.Append(" ORDER BY ") 114 | bldr.AppendItems(b.orderBy, ", ") 115 | } 116 | 117 | // LIMIT 118 | if b.limit != 0 { 119 | bldr.Append(" LIMIT ") 120 | bldr.AppendValue(b.limit) 121 | } 122 | 123 | // Offset 124 | if b.offset != 0 { 125 | bldr.Append(" OFFSET ") 126 | bldr.AppendValue(b.offset) 127 | } 128 | return 129 | } 130 | 131 | type updateValue struct { 132 | col Column 133 | val literal 134 | } 135 | 136 | func newUpdateValue(col Column, val interface{}) updateValue { 137 | return updateValue{ 138 | col: col, 139 | val: toLiteral(val), 140 | } 141 | } 142 | 143 | func (m updateValue) serialize(bldr *builder) { 144 | if !m.col.acceptType(m.val) { 145 | bldr.SetError(newError("%s column not accept %T.", 146 | m.col.config().Type().String(), 147 | m.val.Raw(), 148 | )) 149 | return 150 | } 151 | 152 | bldr.Append(dialect().QuoteField(m.col.column_name())) 153 | bldr.Append("=") 154 | bldr.AppendItem(m.val) 155 | } 156 | -------------------------------------------------------------------------------- /integration_test/main_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder_integration 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | _ "github.com/lib/pq" 12 | _ "github.com/mattn/go-sqlite3" 13 | sb "github.com/umisama/go-sqlbuilder" 14 | "github.com/umisama/go-sqlbuilder/dialects" 15 | _ "github.com/ziutek/mymysql/godrv" 16 | ) 17 | 18 | var db *sql.DB 19 | 20 | // Table for testing 21 | var ( 22 | tbl_person, tbl_phone, tbl_email sb.Table 23 | ) 24 | 25 | // Data for testing 26 | type Person struct { 27 | Id int 28 | Name string 29 | Birth time.Time 30 | } 31 | 32 | type Phone struct { 33 | PersonId int 34 | Number string 35 | } 36 | 37 | type Email struct { 38 | PersonId int 39 | Address string 40 | } 41 | 42 | var persons = []Person{{ 43 | Id: 1, 44 | Name: "Rintaro Okabe", 45 | Birth: time.Date(1991, time.December, 14, 0, 0, 0, 0, time.UTC), 46 | }, { 47 | Id: 2, 48 | Name: "Mayuri Shiina", 49 | Birth: time.Date(1994, time.February, 1, 0, 0, 0, 0, time.UTC), 50 | }, { 51 | Id: 3, 52 | Name: "Itaru Hashida", 53 | Birth: time.Date(1991, time.May, 19, 0, 0, 0, 0, time.UTC), 54 | }} 55 | 56 | var phones = []Phone{{ 57 | PersonId: 1, 58 | Number: "000-0000-0000", 59 | }, { 60 | PersonId: 2, 61 | Number: "111-1111-1111", 62 | }, { 63 | PersonId: 2, 64 | Number: "111-1111-2222", 65 | }} 66 | 67 | var emails = []Email{{ 68 | PersonId: 1, 69 | Address: "sg-epk@jtk93.x29.jp", 70 | }, { 71 | PersonId: 1, 72 | Address: "okarin@example.org", 73 | }, { 74 | PersonId: 2, 75 | Address: "mayusii@example.org", 76 | }, { 77 | PersonId: 3, 78 | Address: "hashida@example.org", 79 | }} 80 | 81 | func TestMain(m *testing.M) { 82 | results := make(map[string]int) 83 | type testcase struct { 84 | name string 85 | dialect sb.Dialect 86 | driver string 87 | dsn string 88 | } 89 | 90 | var cases = []testcase{ 91 | {"sqlite", dialects.Sqlite{}, "sqlite3", ":memory:"}, 92 | {"mysql(ziutek/mymysql)", dialects.MySql{}, "mymysql", "go_sqlbuilder_test1/root/"}, 93 | {"mysql(go-sql-driver/mysql)", dialects.MySql{}, "mysql", "root:@/go_sqlbuilder_test2?parseTime=true"}, 94 | {"postgres", dialects.Postgresql{}, "postgres", "user=postgres dbname=go_sqlbuilder_test sslmode=disable"}, 95 | } 96 | 97 | for _, c := range cases { 98 | fmt.Println("START unit test for", c.name) 99 | 100 | // tables 101 | tbl_person = sb.NewTable( 102 | "PERSON", nil, 103 | sb.IntColumn("id", &sb.ColumnOption{ 104 | PrimaryKey: true, 105 | }), 106 | sb.StringColumn("name", &sb.ColumnOption{ 107 | Unique: true, 108 | Size: 255, 109 | Default: "default_name", 110 | }), 111 | sb.DateColumn("birth", nil), 112 | ) 113 | tbl_phone = sb.NewTable( 114 | "PHONE", 115 | &sb.TableOption{ 116 | Unique: [][]string{{"phone_id", "number"}}, 117 | }, 118 | sb.IntColumn("id", &sb.ColumnOption{ 119 | PrimaryKey: true, 120 | AutoIncrement: true, 121 | }), 122 | sb.IntColumn("person_id", nil), 123 | sb.StringColumn("number", &sb.ColumnOption{ 124 | Size: 255, 125 | }), 126 | ) 127 | tbl_email = sb.NewTable( 128 | "EMAIL", 129 | &sb.TableOption{ 130 | Unique: [][]string{{"person_id", "address"}}, 131 | }, 132 | sb.IntColumn("id", &sb.ColumnOption{ 133 | PrimaryKey: true, 134 | AutoIncrement: true, 135 | }), 136 | sb.IntColumn("person_id", nil), 137 | sb.StringColumn("address", &sb.ColumnOption{ 138 | Size: 255, 139 | }), 140 | ) 141 | 142 | var err error 143 | db, err = sql.Open(c.driver, c.dsn) 144 | if err != nil { 145 | fmt.Println(err.Error()) 146 | } 147 | sb.SetDialect(c.dialect) 148 | 149 | results[c.name] = m.Run() 150 | } 151 | 152 | for _, v := range results { 153 | if v != 0 { 154 | os.Exit(v) 155 | } 156 | } 157 | os.Exit(0) 158 | } 159 | -------------------------------------------------------------------------------- /literal_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestLiteralConvert(t *testing.T) { 10 | str := "makise-kurisu" 11 | var cases = []struct { 12 | lit literal 13 | out interface{} 14 | errmes string 15 | }{ 16 | { 17 | lit: toLiteral(int(10)), 18 | out: int64(10), 19 | errmes: "", 20 | }, { 21 | lit: toLiteral(int64(10)), 22 | out: int64(10), 23 | errmes: "", 24 | }, { 25 | lit: toLiteral(uint(10)), 26 | out: int64(10), 27 | errmes: "", 28 | }, { 29 | lit: toLiteral(uint64(10)), 30 | out: int64(10), 31 | errmes: "", 32 | }, { 33 | lit: toLiteral(float32(10)), 34 | out: float64(10), 35 | errmes: "", 36 | }, { 37 | lit: toLiteral(float64(10)), 38 | out: float64(10), 39 | errmes: "", 40 | }, { 41 | lit: toLiteral(bool(true)), 42 | out: bool(true), 43 | errmes: "", 44 | }, { 45 | lit: toLiteral([]byte{0x11}), 46 | out: []byte{0x11}, 47 | errmes: "", 48 | }, { 49 | lit: toLiteral(string("makise-kurisu")), 50 | out: string("makise-kurisu"), 51 | errmes: "", 52 | }, { 53 | lit: toLiteral(&str), 54 | out: str, 55 | errmes: "", 56 | }, { 57 | lit: toLiteral((*string)(nil)), 58 | out: nil, 59 | errmes: "", 60 | }, { 61 | lit: toLiteral(time.Unix(0, 0)), 62 | out: time.Unix(0, 0), 63 | errmes: "", 64 | }, { 65 | lit: toLiteral(nil), 66 | out: nil, 67 | errmes: "", 68 | }, { 69 | lit: toLiteral(complex(0, 0)), 70 | out: nil, 71 | errmes: "sqlbuilder: got complex128 type, but literal is not supporting this.", 72 | }} 73 | 74 | for num, c := range cases { 75 | val, err := c.lit.(*literalImpl).converted() 76 | if !reflect.DeepEqual(c.out, val) { 77 | t.Errorf("failed on %d", num) 78 | } 79 | if len(c.errmes) != 0 { 80 | if err == nil { 81 | t.Errorf("failed on %d", num) 82 | } 83 | if err.Error() != c.errmes { 84 | t.Errorf("failed on %d", num) 85 | panic(err.Error()) 86 | } 87 | } else { 88 | if err != nil { 89 | t.Errorf("failed on %d", num) 90 | } 91 | } 92 | } 93 | } 94 | 95 | func TestLiteralString(t *testing.T) { 96 | var cases = []struct { 97 | lit literal 98 | out string 99 | errmes string 100 | }{ 101 | { 102 | lit: toLiteral(int(10)), 103 | out: "10", 104 | errmes: "", 105 | }, { 106 | lit: toLiteral(int64(10)), 107 | out: "10", 108 | errmes: "", 109 | }, { 110 | lit: toLiteral(uint(10)), 111 | out: "10", 112 | errmes: "", 113 | }, { 114 | lit: toLiteral(uint64(10)), 115 | out: "10", 116 | errmes: "", 117 | }, { 118 | lit: toLiteral(float32(10)), 119 | out: "10.0000000000", 120 | errmes: "", 121 | }, { 122 | lit: toLiteral(float64(10)), 123 | out: "10.0000000000", 124 | errmes: "", 125 | }, { 126 | lit: toLiteral(bool(true)), 127 | out: "true", 128 | errmes: "", 129 | }, { 130 | lit: toLiteral([]byte{0x11}), 131 | out: string([]byte{0x11}), 132 | errmes: "", 133 | }, { 134 | lit: toLiteral(string("shibuya-rin")), 135 | out: "shibuya-rin", 136 | errmes: "", 137 | }, { 138 | lit: toLiteral(time.Unix(0, 0).UTC()), 139 | out: "1970-01-01 00:00:00", 140 | errmes: "", 141 | }, { 142 | lit: toLiteral(nil), 143 | out: "NULL", 144 | errmes: "", 145 | }, { 146 | lit: toLiteral(complex(0, 0)), 147 | out: "", 148 | errmes: "aaa", 149 | }} 150 | 151 | for num, c := range cases { 152 | val := c.lit.(*literalImpl).string() 153 | if c.out != val { 154 | t.Error("failed on %d", num) 155 | } 156 | } 157 | } 158 | 159 | func TestLiteralIsNil(t *testing.T) { 160 | var cases = []struct { 161 | in literal 162 | out bool 163 | }{ 164 | {toLiteral(int(10)), false}, 165 | {toLiteral([]byte{}), false}, 166 | {toLiteral(nil), true}, 167 | {toLiteral([]byte(nil)), true}, 168 | } 169 | 170 | for num, c := range cases { 171 | isnil := c.in.IsNil() 172 | if c.out != isnil { 173 | t.Error("failed on %d", num) 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /select_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSelect(t *testing.T) { 9 | table1 := NewTable( 10 | "TABLE_A", 11 | &TableOption{}, 12 | IntColumn("id", &ColumnOption{ 13 | PrimaryKey: true, 14 | }), 15 | IntColumn("test1", nil), 16 | IntColumn("test2", nil), 17 | ) 18 | table2 := NewTable( 19 | "TABLE_B", 20 | &TableOption{}, 21 | IntColumn("id", &ColumnOption{ 22 | PrimaryKey: true, 23 | }), 24 | ) 25 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 26 | acol_id := table1.C("id").As("tbl1id") 27 | 28 | var cases = []statementTestCase{{ 29 | stmt: Select(table1). 30 | Columns(table1.C("test1"), table1.C("test2")). 31 | Where( 32 | And( 33 | table1.C("id").Eq(1), 34 | table1.C("test1").Eq(2), 35 | )). 36 | Distinct(). 37 | OrderBy(false, table1.C("id")). 38 | GroupBy(table1.C("id")). 39 | Having(table1.C("id").Eq(1)). 40 | Limit(10). 41 | Offset(20), 42 | query: `SELECT DISTINCT "TABLE_A"."test1", "TABLE_A"."test2" ` + 43 | `FROM "TABLE_A" WHERE "TABLE_A"."id"=? AND "TABLE_A"."test1"=? ` + 44 | `GROUP BY "TABLE_A"."id" HAVING "TABLE_A"."id"=? ORDER BY "TABLE_A"."id" ASC ` + 45 | `LIMIT ? OFFSET ?;`, 46 | args: []interface{}{int64(1), int64(2), int64(1), 10, 20}, 47 | errmsg: "", 48 | }, { 49 | stmt: Select(table1). 50 | Columns(table1.C("test1"), table1.C("test2")), 51 | query: `SELECT "TABLE_A"."test1", "TABLE_A"."test2" FROM "TABLE_A";`, 52 | args: []interface{}{}, 53 | errmsg: "", 54 | }, { 55 | stmt: Select(table1).Columns(acol_id). 56 | Where(acol_id.Eq(1)), 57 | query: `SELECT "TABLE_A"."id" AS "tbl1id" FROM "TABLE_A" WHERE "tbl1id"=?;`, 58 | args: []interface{}{int64(1)}, 59 | errmsg: "", 60 | }, { 61 | stmt: Select(table1).Columns(acol_id). 62 | Where(acol_id.Eq(1)). 63 | OrderBy(false, table1.C("test1")). 64 | OrderBy(true, table1.C("test2")), 65 | query: `SELECT "TABLE_A"."id" AS "tbl1id" FROM "TABLE_A" WHERE "tbl1id"=? ORDER BY "TABLE_A"."test1" ASC, "TABLE_A"."test2" DESC;`, 66 | args: []interface{}{int64(1)}, 67 | errmsg: "", 68 | }, { 69 | stmt: Select(table1).Columns(Star), 70 | query: `SELECT * FROM "TABLE_A";`, 71 | args: []interface{}{}, 72 | errmsg: "", 73 | }, { 74 | stmt: Select(table1), 75 | query: `SELECT * FROM "TABLE_A";`, 76 | args: []interface{}{}, 77 | errmsg: "", 78 | }, { 79 | stmt: Select(tableJoined). 80 | Columns(Star), 81 | query: `SELECT * FROM "TABLE_A" INNER JOIN "TABLE_B" ON "TABLE_A"."test1"="TABLE_B"."id";`, 82 | args: []interface{}{}, 83 | errmsg: "", 84 | }, { 85 | stmt: Select(nil). 86 | Columns(table1.C("test1"), table1.C("test2")), 87 | query: ``, 88 | args: []interface{}{}, 89 | errmsg: "sqlbuilder: table is nil.", 90 | }, { 91 | stmt: Select(table1). 92 | Columns(table1.C("test1"), table1.C("test2")). 93 | Having(table1.C("id").Eq(1)), 94 | query: ``, 95 | args: []interface{}{}, 96 | errmsg: "sqlbuilder: GROUP BY clause is not found.", 97 | }} 98 | 99 | for num, c := range cases { 100 | mes, args, ok := c.Run() 101 | if !ok { 102 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 103 | } 104 | } 105 | } 106 | 107 | func TestSubquery(t *testing.T) { 108 | table1 := NewTable( 109 | "TABLE_A", 110 | &TableOption{}, 111 | IntColumn("id", &ColumnOption{ 112 | PrimaryKey: true, 113 | }), 114 | IntColumn("test1", nil), 115 | IntColumn("test2", nil), 116 | ) 117 | 118 | subquery := Select(table1).Columns(table1.C("id")).ToSubquery("SQ1") 119 | query, attrs, err := Select(subquery). 120 | Columns(subquery.C("id")). 121 | Where(subquery.C("id").Eq(1)).ToSql() 122 | 123 | if `SELECT "SQ1"."id" FROM ( SELECT "TABLE_A"."id" FROM "TABLE_A" ) AS SQ1 WHERE "SQ1"."id"=?;` != query { 124 | t.Errorf("failed \ngot %s", query) 125 | } 126 | if !reflect.DeepEqual([]interface{}{int64(1)}, attrs) { 127 | t.Errorf("failed \ngot %#v", attrs) 128 | } 129 | if err != nil { 130 | t.Error("failed \ngot %#v", err.Error()) 131 | } 132 | } 133 | 134 | func BenchmarkSelect(b *testing.B) { 135 | table1 := NewTable( 136 | "TABLE_A", 137 | &TableOption{}, 138 | IntColumn("id", &ColumnOption{ 139 | PrimaryKey: true, 140 | }), 141 | IntColumn("test1", nil), 142 | IntColumn("test2", nil), 143 | ) 144 | 145 | for i := 0; i < b.N; i++ { 146 | Select(table1). 147 | Columns(table1.C("test1"), table1.C("test2")). 148 | Where( 149 | And( 150 | table1.C("id").Eq(1), 151 | table1.C("test1").Eq(2), 152 | )). 153 | ToSql() 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /condition.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // Condition represents a condition for WHERE clause and other. 4 | type Condition interface { 5 | serializable 6 | 7 | columns() []Column 8 | } 9 | 10 | type connectCondition struct { 11 | connector string 12 | conds []Condition 13 | } 14 | 15 | func (c *connectCondition) serialize(bldr *builder) { 16 | first := true 17 | for _, cond := range c.conds { 18 | if first { 19 | first = false 20 | } else { 21 | bldr.Append(" " + c.connector + " ") 22 | } 23 | if _, ok := cond.(*connectCondition); ok { 24 | // if condition is AND or OR 25 | bldr.Append("( ") 26 | bldr.AppendItem(cond) 27 | bldr.Append(" )") 28 | } else { 29 | bldr.AppendItem(cond) 30 | } 31 | } 32 | return 33 | } 34 | 35 | func (c *connectCondition) columns() []Column { 36 | list := make([]Column, 0) 37 | for _, cond := range c.conds { 38 | list = append(list, cond.columns()...) 39 | } 40 | return list 41 | } 42 | 43 | // And creates a combined condition with "AND" operator. 44 | func And(conds ...Condition) Condition { 45 | return &connectCondition{ 46 | connector: "AND", 47 | conds: conds, 48 | } 49 | } 50 | 51 | // And creates a combined condition with "OR" operator. 52 | func Or(conds ...Condition) Condition { 53 | return &connectCondition{ 54 | connector: "OR", 55 | conds: conds, 56 | } 57 | } 58 | 59 | type binaryOperationCondition struct { 60 | left serializable 61 | right serializable 62 | operator string 63 | err error 64 | } 65 | 66 | func newBinaryOperationCondition(left, right interface{}, operator string) *binaryOperationCondition { 67 | cond := &binaryOperationCondition{ 68 | operator: operator, 69 | } 70 | column_exist := false 71 | switch t := left.(type) { 72 | case Column: 73 | column_exist = true 74 | cond.left = t 75 | case nil: 76 | cond.err = newError("left-hand side of binary operator is null.") 77 | default: 78 | cond.left = toLiteral(t) 79 | } 80 | switch t := right.(type) { 81 | case Column: 82 | column_exist = true 83 | cond.right = t 84 | default: 85 | cond.right = toLiteral(t) 86 | } 87 | if !column_exist { 88 | cond.err = newError("binary operation is need column.") 89 | } 90 | 91 | return cond 92 | } 93 | 94 | func newBetweenCondition(left Column, low, high interface{}) Condition { 95 | low_literal := toLiteral(low) 96 | high_literal := toLiteral(high) 97 | 98 | return &betweenCondition{ 99 | left: left, 100 | lower: low_literal, 101 | higher: high_literal, 102 | } 103 | } 104 | 105 | func (c *binaryOperationCondition) serialize(bldr *builder) { 106 | bldr.AppendItem(c.left) 107 | 108 | switch t := c.right.(type) { 109 | case literal: 110 | if t.IsNil() { 111 | switch c.operator { 112 | case "=": 113 | bldr.Append(" IS ") 114 | case "<>": 115 | bldr.Append(" IS NOT ") 116 | default: 117 | bldr.SetError(newError("NULL can not be used with %s operator.", c.operator)) 118 | } 119 | bldr.Append("NULL") 120 | } else { 121 | bldr.Append(c.operator) 122 | bldr.AppendItem(c.right) 123 | } 124 | default: 125 | bldr.Append(c.operator) 126 | bldr.AppendItem(c.right) 127 | } 128 | return 129 | } 130 | 131 | func (c *binaryOperationCondition) columns() []Column { 132 | list := make([]Column, 0) 133 | if col, ok := c.left.(Column); ok { 134 | list = append(list, col) 135 | } 136 | if col, ok := c.right.(Column); ok { 137 | list = append(list, col) 138 | } 139 | return list 140 | } 141 | 142 | type betweenCondition struct { 143 | left serializable 144 | lower serializable 145 | higher serializable 146 | } 147 | 148 | func (c *betweenCondition) serialize(bldr *builder) { 149 | bldr.AppendItem(c.left) 150 | bldr.Append(" BETWEEN ") 151 | bldr.AppendItem(c.lower) 152 | bldr.Append(" AND ") 153 | bldr.AppendItem(c.higher) 154 | return 155 | } 156 | 157 | func (c *betweenCondition) columns() []Column { 158 | list := make([]Column, 0) 159 | if col, ok := c.left.(Column); ok { 160 | list = append(list, col) 161 | } 162 | if col, ok := c.lower.(Column); ok { 163 | list = append(list, col) 164 | } 165 | if col, ok := c.higher.(Column); ok { 166 | list = append(list, col) 167 | } 168 | return list 169 | } 170 | 171 | type inCondition struct { 172 | left serializable 173 | in []serializable 174 | } 175 | 176 | func newInCondition(left Column, list ...interface{}) Condition { 177 | m := &inCondition{ 178 | left: left, 179 | in: make([]serializable, 0, len(list)), 180 | } 181 | for _, item := range list { 182 | if c, ok := item.(Column); ok { 183 | m.in = append(m.in, c) 184 | } else { 185 | m.in = append(m.in, toLiteral(item)) 186 | } 187 | } 188 | return m 189 | } 190 | 191 | func (c *inCondition) serialize(bldr *builder) { 192 | bldr.AppendItem(c.left) 193 | bldr.Append(" IN ( ") 194 | bldr.AppendItems(c.in, ", ") 195 | bldr.Append(" )") 196 | } 197 | 198 | func (c *inCondition) columns() []Column { 199 | list := make([]Column, 0) 200 | if col, ok := c.left.(Column); ok { 201 | list = append(list, col) 202 | } 203 | for _, in := range c.in { 204 | if col, ok := in.(Column); ok { 205 | list = append(list, col) 206 | } 207 | } 208 | return list 209 | } 210 | -------------------------------------------------------------------------------- /sqlbuilder_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | errs "errors" 5 | "fmt" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "database/sql" 11 | _ "github.com/mattn/go-sqlite3" 12 | ) 13 | 14 | func TestMain(m *testing.M) { 15 | SetDialect(TestDialect{}) 16 | os.Exit(m.Run()) 17 | } 18 | 19 | func TestError(t *testing.T) { 20 | err := newError("hogehogestring") 21 | if "sqlbuilder: hogehogestring" != err.Error() { 22 | t.Errorf("failed\ngot %s", err.Error) 23 | } 24 | } 25 | 26 | func ExampleScenario() { 27 | db, err := sql.Open("sqlite3", ":memory:") 28 | if err != nil { 29 | fmt.Println(err.Error()) 30 | return 31 | } 32 | 33 | // Set dialect first 34 | // dialects are in github.com/umisama/go-sqlbuilder/dialects 35 | SetDialect(TestDialect{}) 36 | 37 | // Define a table 38 | tbl_person := NewTable( 39 | "PERSON", 40 | &TableOption{}, 41 | IntColumn("id", &ColumnOption{ 42 | PrimaryKey: true, 43 | }), 44 | StringColumn("name", &ColumnOption{ 45 | Unique: true, 46 | Size: 255, 47 | Default: "no_name", 48 | }), 49 | DateColumn("birth", nil), 50 | ) 51 | 52 | // Create Table 53 | query, args, err := CreateTable(tbl_person).ToSql() 54 | if err != nil { 55 | fmt.Println(err.Error()) 56 | return 57 | } 58 | _, err = db.Exec(query, args...) 59 | if err != nil { 60 | fmt.Println(err.Error()) 61 | return 62 | } 63 | 64 | // Insert data 65 | // (Table).C function returns a column object. 66 | query, args, err = Insert(tbl_person). 67 | Set(tbl_person.C("name"), "Kurisu Makise"). 68 | Set(tbl_person.C("birth"), time.Date(1992, time.July, 25, 0, 0, 0, 0, time.UTC)). 69 | ToSql() 70 | _, err = db.Exec(query, args...) 71 | if err != nil { 72 | fmt.Println(err.Error()) 73 | return 74 | } 75 | 76 | // Query 77 | var birth time.Time 78 | query, args, err = Select(tbl_person).Columns( 79 | tbl_person.C("birth"), 80 | ).Where( 81 | tbl_person.C("name").Eq("Kurisu Makise"), 82 | ).ToSql() 83 | err = db.QueryRow(query, args...).Scan(&birth) 84 | if err != nil { 85 | fmt.Println(err.Error()) 86 | return 87 | } 88 | fmt.Printf("Kurisu's birthday is %s,%d %d", birth.Month().String(), birth.Day(), birth.Year()) 89 | 90 | // Output: 91 | // Kurisu's birthday is July,25 1992 92 | } 93 | 94 | type TestDialect struct{} 95 | 96 | func (m TestDialect) QuerySuffix() string { 97 | return ";" 98 | } 99 | 100 | func (m TestDialect) BindVar(i int) string { 101 | return "?" 102 | } 103 | 104 | func (m TestDialect) QuoteField(field interface{}) string { 105 | str := "" 106 | bracket := true 107 | switch t := field.(type) { 108 | case string: 109 | str = t 110 | case []byte: 111 | str = string(t) 112 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 113 | str = fmt.Sprint(field) 114 | case float32, float64: 115 | str = fmt.Sprint(field) 116 | case time.Time: 117 | str = t.Format("2006-01-02 15:04:05") 118 | case bool: 119 | if t { 120 | str = "TRUE" 121 | } else { 122 | str = "FALSE" 123 | } 124 | bracket = false 125 | case nil: 126 | return "NULL" 127 | bracket = false 128 | } 129 | if bracket { 130 | str = "\"" + str + "\"" 131 | } 132 | return str 133 | } 134 | 135 | func (m TestDialect) ColumnTypeToString(cc ColumnConfig) (string, error) { 136 | if cc.Option().SqlType != "" { 137 | return cc.Option().SqlType, nil 138 | } 139 | 140 | typ := "" 141 | switch cc.Type() { 142 | case ColumnTypeInt: 143 | typ = "INTEGER" 144 | case ColumnTypeString: 145 | typ = "TEXT" 146 | case ColumnTypeDate: 147 | typ = "DATE" 148 | case ColumnTypeFloat: 149 | typ = "REAL" 150 | case ColumnTypeBool: 151 | typ = "BOOLEAN" 152 | case ColumnTypeBytes: 153 | typ = "BLOB" 154 | } 155 | if typ == "" { 156 | return "", errs.New("dialects: unknown column type") 157 | } else { 158 | return typ, nil 159 | } 160 | } 161 | 162 | func (m TestDialect) ColumnOptionToString(co *ColumnOption) (string, error) { 163 | apnd := func(str, opt string) string { 164 | if len(str) != 0 { 165 | str += " " 166 | } 167 | str += opt 168 | return str 169 | } 170 | 171 | opt := "" 172 | if co.PrimaryKey { 173 | opt = apnd(opt, "PRIMARY KEY") 174 | } 175 | if co.AutoIncrement { 176 | opt = apnd(opt, "AUTOINCREMENT") 177 | } 178 | if co.NotNull { 179 | opt = apnd(opt, "NOT NULL") 180 | } 181 | if co.Unique { 182 | opt = apnd(opt, "UNIQUE") 183 | } 184 | 185 | // TestDialect omitted handling DEFAULT keyword 186 | 187 | return opt, nil 188 | } 189 | 190 | func (m TestDialect) TableOptionToString(to *TableOption) (string, error) { 191 | opt := "" 192 | apnd := func(str, opt string) string { 193 | if len(str) != 0 { 194 | str += " " 195 | } 196 | str += opt 197 | return str 198 | } 199 | 200 | if to.Unique != nil { 201 | opt = apnd(opt, m.tableOptionUnique(to.Unique)) 202 | } 203 | return opt, nil 204 | } 205 | 206 | func (m TestDialect) tableOptionUnique(op [][]string) string { 207 | opt := "" 208 | first_op := true 209 | for _, unique := range op { 210 | if first_op { 211 | first_op = false 212 | } else { 213 | opt += " " 214 | } 215 | 216 | opt += "UNIQUE(" 217 | first := true 218 | for _, col := range unique { 219 | if first { 220 | first = false 221 | } else { 222 | opt += ", " 223 | } 224 | opt += m.QuoteField(col) 225 | } 226 | opt += ")" 227 | } 228 | return opt 229 | } 230 | -------------------------------------------------------------------------------- /create.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | // CreateIndexStatement represents a "CREATE INDEX" statement. 4 | type CreateIndexStatement struct { 5 | table Table 6 | columns []Column 7 | name string 8 | ifNotExists bool 9 | 10 | err error 11 | } 12 | 13 | // CreateTableStatement represents a "CREATE TABLE" statement. 14 | type CreateTableStatement struct { 15 | table Table 16 | ifNotExists bool 17 | 18 | err error 19 | } 20 | 21 | // CreateTable returns new "CREATE TABLE" statement. The table is Table object to create. 22 | func CreateTable(tbl Table) *CreateTableStatement { 23 | if tbl == nil { 24 | return &CreateTableStatement{ 25 | err: newError("table is nil."), 26 | } 27 | } 28 | if _, ok := tbl.(*table); !ok { 29 | return &CreateTableStatement{ 30 | err: newError("CreateTable can use only natural table."), 31 | } 32 | } 33 | 34 | return &CreateTableStatement{ 35 | table: tbl, 36 | } 37 | } 38 | 39 | // IfNotExists sets "IF NOT EXISTS" clause. 40 | func (b *CreateTableStatement) IfNotExists() *CreateTableStatement { 41 | if b.err != nil { 42 | return b 43 | } 44 | b.ifNotExists = true 45 | return b 46 | } 47 | 48 | // ToSql generates query string, placeholder arguments, and error. 49 | func (b *CreateTableStatement) ToSql() (query string, args []interface{}, err error) { 50 | bldr := newBuilder() 51 | defer func() { 52 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 53 | }() 54 | if b.err != nil { 55 | bldr.SetError(b.err) 56 | return 57 | } 58 | 59 | bldr.Append("CREATE TABLE ") 60 | if b.ifNotExists { 61 | bldr.Append("IF NOT EXISTS ") 62 | } 63 | bldr.AppendItem(b.table) 64 | 65 | if len(b.table.Columns()) != 0 { 66 | bldr.Append(" ( ") 67 | bldr.AppendItem(createTableColumnList(b.table.Columns())) 68 | bldr.Append(" )") 69 | } else { 70 | bldr.SetError(newError("CreateTableStatement needs one or more columns.")) 71 | return 72 | } 73 | 74 | // table option 75 | if tabopt, err := dialect().TableOptionToString(b.table.Option()); err == nil { 76 | if len(tabopt) != 0 { 77 | bldr.Append(" " + tabopt) 78 | } 79 | } else { 80 | bldr.SetError(err) 81 | } 82 | 83 | return 84 | } 85 | 86 | // CreateIndex returns new "CREATE INDEX" statement. The table is Table object to create index. 87 | func CreateIndex(tbl Table) *CreateIndexStatement { 88 | if tbl == nil { 89 | return &CreateIndexStatement{ 90 | err: newError("table is nil."), 91 | } 92 | } 93 | if _, ok := tbl.(*table); !ok { 94 | return &CreateIndexStatement{ 95 | err: newError("CreateTable can use only natural table."), 96 | } 97 | } 98 | return &CreateIndexStatement{ 99 | table: tbl, 100 | } 101 | } 102 | 103 | // IfNotExists sets "IF NOT EXISTS" clause. 104 | func (b *CreateIndexStatement) IfNotExists() *CreateIndexStatement { 105 | if b.err != nil { 106 | return b 107 | } 108 | b.ifNotExists = true 109 | return b 110 | } 111 | 112 | // IfNotExists sets "IF NOT EXISTS" clause. If not set this, returns error on ToSql(). 113 | func (b *CreateIndexStatement) Columns(columns ...Column) *CreateIndexStatement { 114 | if b.err != nil { 115 | return b 116 | } 117 | b.columns = columns 118 | return b 119 | } 120 | 121 | // Name sets name for index. 122 | // If not set this, auto generated name will be used. 123 | func (b *CreateIndexStatement) Name(name string) *CreateIndexStatement { 124 | if b.err != nil { 125 | return b 126 | } 127 | b.name = name 128 | return b 129 | } 130 | 131 | // ToSql generates query string, placeholder arguments, and returns err on errors. 132 | func (b *CreateIndexStatement) ToSql() (query string, args []interface{}, err error) { 133 | bldr := newBuilder() 134 | defer func() { 135 | query, args, err = bldr.Query(), bldr.Args(), bldr.Err() 136 | }() 137 | if b.err != nil { 138 | bldr.SetError(b.err) 139 | return 140 | } 141 | 142 | bldr.Append("CREATE INDEX ") 143 | if b.ifNotExists { 144 | bldr.Append("IF NOT EXISTS ") 145 | } 146 | 147 | if len(b.name) != 0 { 148 | bldr.Append(dialect().QuoteField(b.name)) 149 | } else { 150 | bldr.SetError(newError("name was not setted.")) 151 | return 152 | } 153 | 154 | bldr.Append(" ON ") 155 | bldr.AppendItem(b.table) 156 | 157 | if len(b.columns) != 0 { 158 | bldr.Append(" ( ") 159 | bldr.AppendItem(createIndexColumnList(b.columns)) 160 | bldr.Append(" )") 161 | } else { 162 | bldr.SetError(newError("columns was not setted.")) 163 | return 164 | } 165 | return 166 | } 167 | 168 | type createTableColumnList []Column 169 | 170 | func (m createTableColumnList) serialize(bldr *builder) { 171 | first := true 172 | for _, column := range m { 173 | if first { 174 | first = false 175 | } else { 176 | bldr.Append(", ") 177 | } 178 | cc := column.config() 179 | 180 | // Column name 181 | bldr.AppendItem(cc) 182 | bldr.Append(" ") 183 | 184 | // SQL data name 185 | str, err := dialect().ColumnTypeToString(cc) 186 | if err != nil { 187 | bldr.SetError(err) 188 | } 189 | bldr.Append(str) 190 | 191 | str, err = dialect().ColumnOptionToString(cc.Option()) 192 | if err != nil { 193 | bldr.SetError(err) 194 | } 195 | if len(str) != 0 { 196 | bldr.Append(" " + str) 197 | } 198 | } 199 | } 200 | 201 | type createIndexColumnList []Column 202 | 203 | func (m createIndexColumnList) serialize(bldr *builder) { 204 | first := true 205 | for _, column := range m { 206 | if first { 207 | first = false 208 | } else { 209 | bldr.Append(", ") 210 | } 211 | cc := column.config() 212 | bldr.AppendItem(cc) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /alter_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAlterTable(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | }), 14 | IntColumn("test1", nil), 15 | IntColumn("test2", nil), 16 | ) 17 | table2 := NewTable( 18 | "TABLE_B", 19 | &TableOption{}, 20 | IntColumn("id", &ColumnOption{ 21 | PrimaryKey: true, 22 | }), 23 | ) 24 | tableJoined := table1.InnerJoin(table2, table1.C("test1").Eq(table2.C("id"))) 25 | 26 | var cases = []statementTestCase{{ 27 | stmt: AlterTable(table1). 28 | RenameTo("TABLE_AAA"). 29 | AddColumn(IntColumn("test3", nil)). 30 | AddColumn(IntColumn("test4", nil)). 31 | ChangeColumn(table1.C("test1"), IntColumn("test1a", nil)). 32 | DropColumn(table1.C("test1")), 33 | query: `ALTER TABLE "TABLE_A" ADD COLUMN "test3" INTEGER, ADD COLUMN "test4" INTEGER, CHANGE COLUMN "test1" "test1a" INTEGER, DROP COLUMN "test1", RENAME TO "TABLE_AAA";`, 34 | args: []interface{}{}, 35 | errmsg: "", 36 | }, { 37 | stmt: AlterTable(table1).RenameTo("TABLE_AAA"), 38 | query: `ALTER TABLE "TABLE_A" RENAME TO "TABLE_AAA";`, 39 | args: []interface{}{}, 40 | errmsg: "", 41 | }, { 42 | stmt: AlterTable(table1).AddColumn(IntColumn("test3", &ColumnOption{ 43 | Unique: true, 44 | })), 45 | query: `ALTER TABLE "TABLE_A" ADD COLUMN "test3" INTEGER UNIQUE;`, 46 | args: []interface{}{}, 47 | errmsg: "", 48 | }, { 49 | stmt: AlterTable(table1).AddColumnAfter(IntColumn("test0", nil), table1.C("id")), 50 | query: `ALTER TABLE "TABLE_A" ADD COLUMN "test0" INTEGER AFTER "id";`, 51 | args: []interface{}{}, 52 | errmsg: "", 53 | }, { 54 | stmt: AlterTable(table1).AddColumnFirst(IntColumn("test0", nil)), 55 | query: `ALTER TABLE "TABLE_A" ADD COLUMN "test0" INTEGER FIRST;`, 56 | args: []interface{}{}, 57 | errmsg: "", 58 | }, { 59 | stmt: AlterTable(table1).ChangeColumn(table1.C("test1"), IntColumn("test1a", &ColumnOption{ 60 | Unique: true, 61 | })), 62 | query: `ALTER TABLE "TABLE_A" CHANGE COLUMN "test1" "test1a" INTEGER UNIQUE;`, 63 | args: []interface{}{}, 64 | errmsg: "", 65 | }, { 66 | stmt: AlterTable(table1).ChangeColumnFirst(table1.C("test1"), IntColumn("test1a", nil)), 67 | query: `ALTER TABLE "TABLE_A" CHANGE COLUMN "test1" "test1a" INTEGER FIRST;`, 68 | args: []interface{}{}, 69 | errmsg: "", 70 | }, { 71 | stmt: AlterTable(table1).ChangeColumnAfter(table1.C("test1"), IntColumn("test1a", nil), table1.C("test2")), 72 | query: `ALTER TABLE "TABLE_A" CHANGE COLUMN "test1" "test1a" INTEGER AFTER "test2";`, 73 | args: []interface{}{}, 74 | errmsg: "", 75 | }, { 76 | stmt: AlterTable(table1).DropColumn(table1.C("test1")), 77 | query: `ALTER TABLE "TABLE_A" DROP COLUMN "test1";`, 78 | args: []interface{}{}, 79 | errmsg: "", 80 | }, { 81 | stmt: AlterTable(table1).DropColumn(table1.C("invalid")), 82 | query: ``, 83 | args: []interface{}{}, 84 | errmsg: "sqlbuilder: column TABLE_A.invalid was not found.", 85 | }, { 86 | stmt: AlterTable(table1).ChangeColumnAfter(table1.C("invalid"), IntColumn("test1a", nil), table1.C("test2")), 87 | query: ``, 88 | args: []interface{}{}, 89 | errmsg: "sqlbuilder: column TABLE_A.invalid was not found.", 90 | }, { 91 | stmt: AlterTable(table1).ChangeColumnAfter(table1.C("test1"), IntColumn("test1a", nil), table1.C("invalid")), 92 | query: ``, 93 | args: []interface{}{}, 94 | errmsg: "sqlbuilder: column TABLE_A.invalid was not found.", 95 | }, { 96 | stmt: AlterTable(table1).AddColumnAfter(IntColumn("test0", nil), table1.C("invalid")), 97 | query: ``, 98 | args: []interface{}{}, 99 | errmsg: "sqlbuilder: column TABLE_A.invalid was not found.", 100 | }, { 101 | stmt: AlterTable(nil).DropColumn(table1.C("invalid")), 102 | query: ``, 103 | args: []interface{}{}, 104 | errmsg: "sqlbuilder: table is nil.", 105 | }, { 106 | stmt: AlterTable(tableJoined).DropColumn(table1.C("id")), 107 | query: ``, 108 | args: []interface{}{}, 109 | errmsg: "sqlbuilder: AlterTable can use only natural table.", 110 | }} 111 | 112 | for num, c := range cases { 113 | mes, args, ok := c.Run() 114 | if !ok { 115 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 116 | } 117 | } 118 | } 119 | 120 | func TestAlterTableApplyToTable(t *testing.T) { 121 | var cases = []struct { 122 | stmt func(Table) *AlterTableStatement 123 | expect_columns []string 124 | expect_name string 125 | }{{ 126 | stmt: func(t Table) *AlterTableStatement { 127 | return AlterTable(t). 128 | RenameTo("TABLE_AAA"). 129 | AddColumn(IntColumn("test3", nil)). 130 | AddColumnFirst(IntColumn("test4", nil)). 131 | AddColumnAfter(IntColumn("test5", nil), t.C("id")). 132 | ChangeColumn(t.C("test1"), IntColumn("test1a", nil)). 133 | ChangeColumnFirst(t.C("test2"), IntColumn("test2a", nil)). 134 | DropColumn(t.C("id")) 135 | }, 136 | expect_columns: []string{"test2a", "test4", "test5", "test1a", "test3"}, 137 | expect_name: "TABLE_AAA", 138 | }, { 139 | stmt: func(t Table) *AlterTableStatement { 140 | return AlterTable(t). 141 | ChangeColumnAfter(t.C("test1"), IntColumn("test1a", nil), t.C("test2")) 142 | }, 143 | expect_columns: []string{"id", "test2", "test1a"}, 144 | expect_name: "TABLE_A", 145 | }} 146 | 147 | for num, c := range cases { 148 | table1 := NewTable( 149 | "TABLE_A", 150 | &TableOption{}, 151 | IntColumn("id", &ColumnOption{ 152 | PrimaryKey: true, 153 | }), 154 | IntColumn("test1", nil), 155 | IntColumn("test2", nil), 156 | ) 157 | 158 | err := c.stmt(table1).ApplyToTable() 159 | if err != nil { 160 | t.Errorf("failed on %d", num) 161 | } 162 | if len(table1.Columns()) != len(c.expect_columns) { 163 | t.Errorf("failed on %d", num) 164 | } 165 | for i, col := range table1.Columns() { 166 | if c.expect_columns[i] != col.column_name() { 167 | t.Errorf("failed on %d", num) 168 | break 169 | } 170 | } 171 | if table1.Name() != c.expect_name { 172 | t.Errorf("failed on %d", num) 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /condition_test.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBinaryCondition(t *testing.T) { 8 | table1 := NewTable( 9 | "TABLE_A", 10 | &TableOption{}, 11 | IntColumn("id", &ColumnOption{ 12 | PrimaryKey: true, 13 | }), 14 | IntColumn("test1", nil), 15 | IntColumn("test2", nil), 16 | ) 17 | var cases = []conditionTestCase{ 18 | { 19 | cond: table1.C("id").Eq(table1.C("test1")), 20 | query: `"TABLE_A"."id"="TABLE_A"."test1"`, 21 | args: []interface{}{}, 22 | errmsg: "", 23 | }, { 24 | cond: table1.C("id").Eq(1), 25 | query: `"TABLE_A"."id"=?`, 26 | args: []interface{}{int64(1)}, 27 | errmsg: "", 28 | }, { 29 | cond: table1.C("id").NotEq(1), 30 | query: `"TABLE_A"."id"<>?`, 31 | args: []interface{}{int64(1)}, 32 | errmsg: "", 33 | }, { 34 | cond: table1.C("id").Gt(1), 35 | query: `"TABLE_A"."id">?`, 36 | args: []interface{}{int64(1)}, 37 | errmsg: "", 38 | }, { 39 | cond: table1.C("id").GtEq(1), 40 | query: `"TABLE_A"."id">=?`, 41 | args: []interface{}{int64(1)}, 42 | errmsg: "", 43 | }, { 44 | cond: table1.C("id").Lt(1), 45 | query: `"TABLE_A"."id" operator.", 83 | }, { 84 | // case for fail 85 | cond: table1.C("id").In(NewTable("DUMMY TABLE", &TableOption{}, StringColumn("id", nil))), 86 | query: `"TABLE_A"."id" IN ( `, 87 | args: []interface{}{}, 88 | errmsg: "sqlbuilder: got sqlbuilder.table type, but literal is not supporting this.", 89 | }, 90 | } 91 | 92 | for num, c := range cases { 93 | mes, args, ok := c.Run() 94 | if !ok { 95 | t.Errorf(mes+" (case no.%d)", append(args, num)...) 96 | } 97 | } 98 | } 99 | 100 | func TestBinaryConditionForSqlFunctions(t *testing.T) { 101 | table1 := NewTable( 102 | "TABLE_A", 103 | &TableOption{}, 104 | IntColumn("id", &ColumnOption{ 105 | PrimaryKey: true, 106 | }), 107 | IntColumn("test1", nil), 108 | IntColumn("test2", nil), 109 | ) 110 | var cases = []conditionTestCase{ 111 | { 112 | cond: Func("count", table1.C("id")).Eq(table1.C("test1")), 113 | query: `count("TABLE_A"."id")="TABLE_A"."test1"`, 114 | args: []interface{}{}, 115 | errmsg: "", 116 | }, { 117 | cond: Func("count", table1.C("id")).Eq(1), 118 | query: `count("TABLE_A"."id")=?`, 119 | args: []interface{}{int64(1)}, 120 | errmsg: "", 121 | }, { 122 | cond: Func("count", table1.C("id")).NotEq(1), 123 | query: `count("TABLE_A"."id")<>?`, 124 | args: []interface{}{int64(1)}, 125 | errmsg: "", 126 | }, { 127 | cond: Func("count", table1.C("id")).Gt(1), 128 | query: `count("TABLE_A"."id")>?`, 129 | args: []interface{}{int64(1)}, 130 | errmsg: "", 131 | }, { 132 | cond: Func("count", table1.C("id")).GtEq(1), 133 | query: `count("TABLE_A"."id")>=?`, 134 | args: []interface{}{int64(1)}, 135 | errmsg: "", 136 | }, { 137 | cond: Func("count", table1.C("id")).Lt(1), 138 | query: `count("TABLE_A"."id")``` | "TABLE"."id" <> 10 | 318 | |Gt(Column or value) |GRATER-THAN | ```>``` | "TABLE"."id" > 10 | 319 | |GtEq(Column or value) |GRATER-THAN OR EQUAL TO | ```>=``` | "TABLE"."id" >= 10 | 320 | |Lt(Column or value) |LESS-THAN | ```<``` | "TABLE"."id" < 10 | 321 | |LtEq(Column or value) |LESS-THAN OR EQUAL TO | ```<=``` | "TABLE"."id" <= 10 | 322 | |Like(string) |LIKE | ```LIKE``` | "TABLE"."id" LIKE "%hoge%" | 323 | |In(values array) |IN | ```IN``` | "TABLE"."id" IN ( 1, 2, 3 ) | 324 | |Between(loewer, higher int) |BETWEEN | ```BETWEEN``` | "TABLE"."id" BETWEEN 10 AND 20)| 325 | 326 | Document for all: [godoc(Column)](http://godoc.org/github.com/umisama/go-sqlbuilder#Column) 327 | 328 | ## More documents 329 | [godoc.org](http://godoc.org/github.com/umisama/go-sqlbuilder) 330 | 331 | ## License 332 | under the MIT license 333 | -------------------------------------------------------------------------------- /table.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | type joinType int 4 | 5 | const ( 6 | inner_join joinType = iota 7 | left_outer_join 8 | right_outer_join 9 | full_outer_join 10 | ) 11 | 12 | type table struct { 13 | name string 14 | option *TableOption 15 | columns []Column 16 | } 17 | 18 | // TableOption reprecents constraint of a table. 19 | type TableOption struct { 20 | Unique [][]string 21 | //ForeignKey map[string]Column // will implement future 22 | } 23 | 24 | type joinTable struct { 25 | typ joinType 26 | left Table 27 | right Table 28 | on Condition 29 | } 30 | 31 | // Table represents a table. 32 | type Table interface { 33 | serializable 34 | 35 | // C returns table's column by the name. 36 | C(name string) Column 37 | 38 | // Name returns table' name. 39 | // returns empty if it is joined table or subquery. 40 | Name() string 41 | 42 | // Option returns table's option(table constraint). 43 | // returns nil if it is joined table or subquery. 44 | Option() *TableOption 45 | 46 | // Columns returns all columns. 47 | Columns() []Column 48 | 49 | // InnerJoin returns a joined table use with "INNER JOIN" clause. 50 | // The joined table can be handled in same way as single table. 51 | InnerJoin(Table, Condition) Table 52 | 53 | // LeftOuterJoin returns a joined table use with "LEFT OUTER JOIN" clause. 54 | // The joined table can be handled in same way as single table. 55 | LeftOuterJoin(Table, Condition) Table 56 | 57 | // RightOuterJoin returns a joined table use with "RIGHT OUTER JOIN" clause. 58 | // The joined table can be handled in same way as single table. 59 | RightOuterJoin(Table, Condition) Table 60 | 61 | // FullOuterJoin returns a joined table use with "FULL OUTER JOIN" clause. 62 | // The joined table can be handled in same way as single table. 63 | FullOuterJoin(Table, Condition) Table 64 | 65 | hasColumn(column Column) bool 66 | } 67 | 68 | // NewTable returns a new table named by the name. Specify table columns by the column_config. 69 | // Panic if column is empty. 70 | func NewTable(name string, option *TableOption, column_configs ...ColumnConfig) Table { 71 | if len(column_configs) == 0 { 72 | panic(newError("column is needed.")) 73 | } 74 | if option == nil { 75 | option = &TableOption{} 76 | } 77 | 78 | t := &table{ 79 | name: name, 80 | option: option, 81 | columns: make([]Column, 0, len(column_configs)), 82 | } 83 | 84 | for _, column_config := range column_configs { 85 | err := t.AddColumnLast(column_config) 86 | if err != nil { 87 | panic(err) 88 | } 89 | } 90 | 91 | return t 92 | } 93 | 94 | func (m *table) serialize(bldr *builder) { 95 | bldr.Append(dialect().QuoteField(m.name)) 96 | return 97 | } 98 | 99 | func (m *table) C(name string) Column { 100 | for _, column := range m.columns { 101 | if column.column_name() == name { 102 | return column 103 | } 104 | } 105 | 106 | return newErrorColumn(newError("column %s.%s was not found.", m.name, name)) 107 | } 108 | 109 | func (m *table) Name() string { 110 | return m.name 111 | } 112 | 113 | func (m *table) SetName(name string) { 114 | m.name = name 115 | } 116 | 117 | func (m *table) Columns() []Column { 118 | return m.columns 119 | } 120 | 121 | func (m *table) Option() *TableOption { 122 | return m.option 123 | } 124 | 125 | func (m *table) AddColumnLast(cc ColumnConfig) error { 126 | return m.addColumn(cc, len(m.columns)) 127 | } 128 | 129 | func (m *table) AddColumnFirst(cc ColumnConfig) error { 130 | return m.addColumn(cc, 0) 131 | } 132 | 133 | func (m *table) AddColumnAfter(cc ColumnConfig, after Column) error { 134 | for i := range m.columns { 135 | if m.columns[i] == after { 136 | return m.addColumn(cc, i+1) 137 | } 138 | } 139 | return newError("column not found.") 140 | } 141 | 142 | func (m *table) ChangeColumn(trg Column, cc ColumnConfig) error { 143 | for i := range m.columns { 144 | if m.columns[i] == trg { 145 | err := m.dropColumn(i) 146 | if err != nil { 147 | return err 148 | } 149 | err = m.addColumn(cc, i) 150 | if err != nil { 151 | return err 152 | } 153 | return nil 154 | } 155 | } 156 | return newError("column not found.") 157 | } 158 | 159 | func (m *table) ChangeColumnFirst(trg Column, cc ColumnConfig) error { 160 | for i := range m.columns { 161 | if m.columns[i] == trg { 162 | err := m.dropColumn(i) 163 | if err != nil { 164 | return err 165 | } 166 | err = m.addColumn(cc, 0) 167 | if err != nil { 168 | return err 169 | } 170 | return nil 171 | } 172 | } 173 | return newError("column not found.") 174 | } 175 | 176 | func (m *table) ChangeColumnAfter(trg Column, cc ColumnConfig, after Column) error { 177 | backup := make([]Column, len(m.columns)) 178 | copy(backup, m.columns) 179 | found := false 180 | for i := range m.columns { 181 | if m.columns[i] == trg { 182 | err := m.dropColumn(i) 183 | if err != nil { 184 | m.columns = backup 185 | return err 186 | } 187 | found = true 188 | break 189 | } 190 | } 191 | if !found { 192 | return newError("column not found.") 193 | } 194 | for i := range m.columns { 195 | if m.columns[i] == after { 196 | err := m.addColumn(cc, i+1) 197 | if err != nil { 198 | m.columns = backup 199 | return err 200 | } 201 | return nil 202 | } 203 | } 204 | m.columns = backup 205 | return newError("column not found.") 206 | } 207 | 208 | func (m *table) addColumn(cc ColumnConfig, pos int) error { 209 | if len(m.columns) < pos || pos < 0 { 210 | return newError("Invalid position.") 211 | } 212 | 213 | var ( 214 | u = make([]Column, pos) 215 | p = make([]Column, len(m.columns)-pos) 216 | ) 217 | copy(u, m.columns[:pos]) 218 | copy(p, m.columns[pos:]) 219 | c := cc.toColumn(m) 220 | m.columns = append(u, c) 221 | m.columns = append(m.columns, p...) 222 | return nil 223 | } 224 | 225 | func (m *table) DropColumn(col Column) error { 226 | for i := range m.columns { 227 | if m.columns[i] == col { 228 | return m.dropColumn(i) 229 | } 230 | } 231 | return newError("column not found.") 232 | } 233 | 234 | func (m *table) dropColumn(pos int) error { 235 | if len(m.columns) < pos || pos < 0 { 236 | return newError("Invalid position.") 237 | } 238 | var ( 239 | u = make([]Column, pos) 240 | p = make([]Column, len(m.columns)-pos-1) 241 | ) 242 | copy(u, m.columns[:pos]) 243 | if len(m.columns) > pos+1 { 244 | copy(p, m.columns[pos+1:]) 245 | } 246 | m.columns = append(u, p...) 247 | return nil 248 | } 249 | 250 | func (m *table) InnerJoin(right Table, on Condition) Table { 251 | return &joinTable{ 252 | left: m, 253 | right: right, 254 | typ: inner_join, 255 | on: on, 256 | } 257 | } 258 | 259 | func (m *table) LeftOuterJoin(right Table, on Condition) Table { 260 | return &joinTable{ 261 | left: m, 262 | right: right, 263 | typ: left_outer_join, 264 | on: on, 265 | } 266 | } 267 | 268 | func (m *table) RightOuterJoin(right Table, on Condition) Table { 269 | return &joinTable{ 270 | left: m, 271 | right: right, 272 | typ: right_outer_join, 273 | on: on, 274 | } 275 | } 276 | 277 | func (m *table) FullOuterJoin(right Table, on Condition) Table { 278 | return &joinTable{ 279 | left: m, 280 | right: right, 281 | typ: full_outer_join, 282 | on: on, 283 | } 284 | } 285 | 286 | func (m *table) hasColumn(trg Column) bool { 287 | if cimpl, ok := trg.(*columnImpl); ok { 288 | if trg == Star { 289 | return true 290 | } 291 | for _, col := range m.columns { 292 | if col == cimpl { 293 | return true 294 | } 295 | } 296 | return false 297 | } 298 | if acol, ok := trg.(*aliasColumn); ok { 299 | for _, col := range m.columns { 300 | if col == acol.column { 301 | return true 302 | } 303 | } 304 | return false 305 | } 306 | if sqlfn, ok := trg.(*sqlFuncImpl); ok { 307 | for _, fncol := range sqlfn.columns() { 308 | find := false 309 | for _, col := range m.columns { 310 | if col == fncol { 311 | find = true 312 | } 313 | } 314 | if !find { 315 | return false 316 | } 317 | } 318 | return true 319 | } 320 | return false 321 | } 322 | 323 | func (m *joinTable) C(name string) Column { 324 | l_col := m.left.C(name) 325 | r_col := m.right.C(name) 326 | 327 | _, l_err := l_col.(*errorColumn) 328 | _, r_err := r_col.(*errorColumn) 329 | 330 | switch { 331 | case l_err && r_err: 332 | return newErrorColumn(newError("column %s was not found.", name)) 333 | case l_err && !r_err: 334 | return r_col 335 | case !l_err && r_err: 336 | return l_col 337 | default: 338 | return newErrorColumn(newError("column %s was duplicated.", name)) 339 | } 340 | } 341 | 342 | func (m *joinTable) Name() string { 343 | return "" 344 | } 345 | 346 | func (m *joinTable) Columns() []Column { 347 | return append(m.left.Columns(), m.right.Columns()...) 348 | } 349 | 350 | func (m *joinTable) Option() *TableOption { 351 | return nil 352 | } 353 | 354 | func (m *joinTable) InnerJoin(right Table, on Condition) Table { 355 | return &joinTable{ 356 | left: m, 357 | right: right, 358 | typ: inner_join, 359 | on: on, 360 | } 361 | } 362 | 363 | func (m *joinTable) LeftOuterJoin(right Table, on Condition) Table { 364 | return &joinTable{ 365 | left: m, 366 | right: right, 367 | typ: left_outer_join, 368 | on: on, 369 | } 370 | } 371 | 372 | func (m *joinTable) RightOuterJoin(right Table, on Condition) Table { 373 | return &joinTable{ 374 | left: m, 375 | right: right, 376 | typ: right_outer_join, 377 | on: on, 378 | } 379 | } 380 | 381 | func (m *joinTable) FullOuterJoin(right Table, on Condition) Table { 382 | return &joinTable{ 383 | left: m, 384 | right: right, 385 | typ: full_outer_join, 386 | on: on, 387 | } 388 | } 389 | 390 | func (m *joinTable) serialize(bldr *builder) { 391 | bldr.AppendItem(m.left) 392 | switch m.typ { 393 | case inner_join: 394 | bldr.Append(" INNER JOIN ") 395 | case left_outer_join: 396 | bldr.Append(" LEFT OUTER JOIN ") 397 | case right_outer_join: 398 | bldr.Append(" RIGHT OUTER JOIN ") 399 | case full_outer_join: 400 | bldr.Append(" FULL OUTER JOIN ") 401 | } 402 | bldr.AppendItem(m.right) 403 | bldr.Append(" ON ") 404 | bldr.AppendItem(m.on) 405 | return 406 | } 407 | 408 | func (m *joinTable) hasColumn(trg Column) bool { 409 | if m.left.hasColumn(trg) { 410 | return true 411 | } 412 | if m.right.hasColumn(trg) { 413 | return true 414 | } 415 | return false 416 | } 417 | -------------------------------------------------------------------------------- /column.go: -------------------------------------------------------------------------------- 1 | package sqlbuilder 2 | 3 | import ( 4 | sqldriver "database/sql/driver" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | // ColumnConfig represents a config for table's column. 10 | // This has a name, data type and some options. 11 | type ColumnConfig interface { 12 | serializable 13 | 14 | toColumn(Table) Column 15 | Name() string 16 | Type() ColumnType 17 | Option() *ColumnOption 18 | } 19 | 20 | // ColumnType reprecents a type of column. 21 | // Dialects handle this for know column options. 22 | type ColumnType int 23 | 24 | const ( 25 | ColumnTypeAny ColumnType = iota 26 | ColumnTypeInt 27 | ColumnTypeString 28 | ColumnTypeDate 29 | ColumnTypeFloat 30 | ColumnTypeBool 31 | ColumnTypeBytes 32 | ) 33 | 34 | func (t ColumnType) String() string { 35 | switch t { 36 | case ColumnTypeInt: 37 | return "int" 38 | case ColumnTypeString: 39 | return "string" 40 | case ColumnTypeDate: 41 | return "date" 42 | case ColumnTypeFloat: 43 | return "float" 44 | case ColumnTypeBool: 45 | return "bool" 46 | case ColumnTypeBytes: 47 | return "bytes" 48 | case ColumnTypeAny: 49 | return "any" 50 | } 51 | panic(newError("unknown columnType")) 52 | } 53 | 54 | func (t ColumnType) CapableTypes() []reflect.Type { 55 | switch t { 56 | case ColumnTypeInt: 57 | return []reflect.Type{ 58 | reflect.TypeOf(int(0)), 59 | reflect.TypeOf(int8(0)), 60 | reflect.TypeOf(int16(0)), 61 | reflect.TypeOf(int32(0)), 62 | reflect.TypeOf(int64(0)), 63 | reflect.TypeOf(uint(0)), 64 | reflect.TypeOf(uint8(0)), 65 | reflect.TypeOf(uint16(0)), 66 | reflect.TypeOf(uint32(0)), 67 | reflect.TypeOf(uint64(0)), 68 | } 69 | case ColumnTypeString: 70 | return []reflect.Type{ 71 | reflect.TypeOf(""), 72 | } 73 | case ColumnTypeDate: 74 | return []reflect.Type{ 75 | reflect.TypeOf(time.Time{}), 76 | } 77 | case ColumnTypeFloat: 78 | return []reflect.Type{ 79 | reflect.TypeOf(float32(0)), 80 | reflect.TypeOf(float64(0)), 81 | } 82 | case ColumnTypeBool: 83 | return []reflect.Type{ 84 | reflect.TypeOf(bool(true)), 85 | } 86 | case ColumnTypeBytes: 87 | return []reflect.Type{ 88 | reflect.TypeOf([]byte{}), 89 | } 90 | case ColumnTypeAny: 91 | return []reflect.Type{} // but accept all types 92 | } 93 | return []reflect.Type{} 94 | } 95 | 96 | // ColumnOption represents option for a column. ex: primary key. 97 | type ColumnOption struct { 98 | PrimaryKey bool 99 | NotNull bool 100 | Unique bool 101 | AutoIncrement bool 102 | Size int 103 | SqlType string 104 | Default interface{} 105 | } 106 | 107 | // ColumnList represents list of Column. 108 | type ColumnList []Column 109 | 110 | // Column represents a table column. 111 | type Column interface { 112 | serializable 113 | 114 | column_name() string 115 | config() ColumnConfig 116 | acceptType(interface{}) bool 117 | 118 | // As creates Column alias. 119 | As(alias string) Column 120 | 121 | // Eq creates Condition for "column==right". Type for right is column's one or other Column. 122 | Eq(right interface{}) Condition 123 | 124 | // NotEq creates Condition for "column<>right". Type for right is column's one or other Column. 125 | NotEq(right interface{}) Condition 126 | 127 | // GtEq creates Condition for "column>right". Type for right is column's one or other Column. 128 | Gt(right interface{}) Condition 129 | 130 | // GtEq creates Condition for "column>=right". Type for right is column's one or other Column. 131 | GtEq(right interface{}) Condition 132 | 133 | // Lt creates Condition for "column") 292 | } 293 | 294 | func (left *columnImpl) Gt(right interface{}) Condition { 295 | return newBinaryOperationCondition(left, right, ">") 296 | } 297 | 298 | func (left *columnImpl) GtEq(right interface{}) Condition { 299 | return newBinaryOperationCondition(left, right, ">=") 300 | } 301 | 302 | func (left *columnImpl) Lt(right interface{}) Condition { 303 | return newBinaryOperationCondition(left, right, "<") 304 | } 305 | 306 | func (left *columnImpl) LtEq(right interface{}) Condition { 307 | return newBinaryOperationCondition(left, right, "<=") 308 | } 309 | 310 | func (left *columnImpl) Like(right string) Condition { 311 | return newBinaryOperationCondition(left, right, " LIKE ") 312 | } 313 | 314 | func (left *columnImpl) Between(lower, higher interface{}) Condition { 315 | return newBetweenCondition(left, lower, higher) 316 | } 317 | 318 | func (left *columnImpl) In(val ...interface{}) Condition { 319 | return newInCondition(left, val...) 320 | } 321 | 322 | func (b ColumnList) serialize(bldr *builder) { 323 | first := true 324 | for _, column := range b { 325 | if column == nil { 326 | bldr.SetError(newError("column is not found.")) 327 | return 328 | } 329 | if first { 330 | first = false 331 | } else { 332 | bldr.Append(", ") 333 | } 334 | bldr.Append(dialect().QuoteField(column.column_name())) 335 | } 336 | return 337 | } 338 | 339 | type errorColumn struct { 340 | err error 341 | } 342 | 343 | func newErrorColumn(err error) Column { 344 | return &errorColumn{ 345 | err: err, 346 | } 347 | } 348 | 349 | func (m *errorColumn) column_name() string { 350 | return "" 351 | } 352 | 353 | func (m *errorColumn) config() ColumnConfig { 354 | return nil 355 | } 356 | 357 | func (m *errorColumn) acceptType(interface{}) bool { 358 | return false 359 | } 360 | 361 | func (m *errorColumn) serialize(bldr *builder) { 362 | bldr.SetError(m.err) 363 | return 364 | } 365 | 366 | func (m *errorColumn) As(string) Column { 367 | return m 368 | } 369 | 370 | func (left *errorColumn) Eq(right interface{}) Condition { 371 | return newBinaryOperationCondition(left, right, "=") 372 | } 373 | 374 | func (left *errorColumn) NotEq(right interface{}) Condition { 375 | return newBinaryOperationCondition(left, right, "<>") 376 | } 377 | 378 | func (left *errorColumn) Gt(right interface{}) Condition { 379 | return newBinaryOperationCondition(left, right, ">") 380 | } 381 | 382 | func (left *errorColumn) GtEq(right interface{}) Condition { 383 | return newBinaryOperationCondition(left, right, ">=") 384 | } 385 | 386 | func (left *errorColumn) Lt(right interface{}) Condition { 387 | return newBinaryOperationCondition(left, right, "<") 388 | } 389 | 390 | func (left *errorColumn) LtEq(right interface{}) Condition { 391 | return newBinaryOperationCondition(left, right, "<=") 392 | } 393 | 394 | func (left *errorColumn) Like(right string) Condition { 395 | return newBinaryOperationCondition(left, right, " LIKE ") 396 | } 397 | 398 | func (left *errorColumn) Between(lower, higher interface{}) Condition { 399 | return newBetweenCondition(left, lower, higher) 400 | } 401 | 402 | func (left *errorColumn) In(val ...interface{}) Condition { 403 | return newInCondition(left, val...) 404 | } 405 | 406 | type aliasColumn struct { 407 | column Column 408 | alias string 409 | } 410 | 411 | func (m *aliasColumn) column_name() string { 412 | return m.alias 413 | } 414 | 415 | func (m *aliasColumn) config() ColumnConfig { 416 | return m.column.config() 417 | } 418 | 419 | func (m *aliasColumn) acceptType(val interface{}) bool { 420 | return m.column.acceptType(val) 421 | } 422 | 423 | func (m *aliasColumn) As(alias string) Column { 424 | return &aliasColumn{ 425 | column: m, 426 | alias: alias, 427 | } 428 | } 429 | 430 | func (m *aliasColumn) serialize(bldr *builder) { 431 | bldr.Append(dialect().QuoteField(m.alias)) 432 | return 433 | } 434 | 435 | func (m *aliasColumn) column_alias() string { 436 | return m.alias 437 | } 438 | 439 | func (m *aliasColumn) source() Column { 440 | return m.column 441 | } 442 | 443 | func (left *aliasColumn) Eq(right interface{}) Condition { 444 | return newBinaryOperationCondition(left, right, "=") 445 | } 446 | 447 | func (left *aliasColumn) NotEq(right interface{}) Condition { 448 | return newBinaryOperationCondition(left, right, "<>") 449 | } 450 | 451 | func (left *aliasColumn) Gt(right interface{}) Condition { 452 | return newBinaryOperationCondition(left, right, ">") 453 | } 454 | 455 | func (left *aliasColumn) GtEq(right interface{}) Condition { 456 | return newBinaryOperationCondition(left, right, ">=") 457 | } 458 | 459 | func (left *aliasColumn) Lt(right interface{}) Condition { 460 | return newBinaryOperationCondition(left, right, "<") 461 | } 462 | 463 | func (left *aliasColumn) LtEq(right interface{}) Condition { 464 | return newBinaryOperationCondition(left, right, "<=") 465 | } 466 | 467 | func (left *aliasColumn) Like(right string) Condition { 468 | return newBinaryOperationCondition(left, right, " LIKE ") 469 | } 470 | 471 | func (left *aliasColumn) Between(lower, higher interface{}) Condition { 472 | return newBetweenCondition(left, lower, higher) 473 | } 474 | 475 | func (left *aliasColumn) In(val ...interface{}) Condition { 476 | return newInCondition(left, val...) 477 | } 478 | --------------------------------------------------------------------------------