mirror of https://github.com/lqs/sqlingo
initial commit
This commit is contained in:
commit
1268d4b2ef
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Liu Qishuai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<img src="https://raw.githubusercontent.com/lqs/sqlingo/master/logo.png" width="236" height="106">
|
||||
|
||||
**sqlingo** is a SQL DSL & ORM library in Go. It generates code from your database and lets you write SQL queries easily.
|
||||
|
||||
WARNING: sqlingo is still under development. It's expected to be released by end of November 2018.
|
||||
|
||||
## Tutorial
|
||||
|
||||
### Prepare your database
|
||||
In order to generate code, sqlingo requires your tables are already created in the database.
|
||||
|
||||
### Install sqlingo code generator
|
||||
```
|
||||
$ go get -u github.com/lqs/sqlingo/sqlingo-gen
|
||||
```
|
||||
|
||||
### Generate code for your database
|
||||
```
|
||||
$ mkdir -p generated/sqlingo
|
||||
$ sqlingo-gen root:123456@/database_name >generated/sqlingo/database_name.dsl.go
|
||||
```
|
||||
|
||||
### Start using sqlingo
|
||||
Create `main.go` to use the generated code
|
||||
```
|
||||
import (
|
||||
"github.com/lqs/sqlingo"
|
||||
"./generated/sqlingo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sqlingo.Open("mysql", "root:123456@/database_name")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
# insert some rows
|
||||
customer1 := &CustomerModel{name: "Customer One"}
|
||||
customer2 := &CustomerModel{name: "Customer Two"}
|
||||
db.InsertInto(Customer).
|
||||
Values(customer1, customer2).
|
||||
Execute()
|
||||
|
||||
# do some queries
|
||||
var customers []*CustomerModel
|
||||
db.SelectFrom(Customer).
|
||||
Where(Customer.Id.In(1, 2)).
|
||||
Fetch(&customers)
|
||||
|
||||
# more examples
|
||||
var customerId int64
|
||||
var orderId int64
|
||||
db.Select(Customer.Id, Order.Id).
|
||||
From(Customer, Order).
|
||||
Where(Customer.Id.Equals(Order.CustomerId), Order.Id.Equals(1)).
|
||||
Fetch(&customerId, &orderId)
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package sqlingo
|
||||
|
||||
type Alias interface {
|
||||
GetSQL() string
|
||||
}
|
||||
|
||||
type alias struct {
|
||||
expression Expression
|
||||
name string
|
||||
}
|
||||
|
||||
func (a *alias) GetSQL() string {
|
||||
return a.expression.GetSQL() + " AS " + getSQLForName(a.name)
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Model interface {
|
||||
GetTable() Table
|
||||
GetValues() []interface{}
|
||||
}
|
||||
|
||||
type Assignment interface {
|
||||
GetSQL() string
|
||||
}
|
||||
|
||||
type assignment struct {
|
||||
Assignment
|
||||
field Field
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (a *assignment) GetSQL() string {
|
||||
value, _ := getSQLFromWhatever(a.value)
|
||||
return a.field.GetSQL() + " = " + value
|
||||
}
|
||||
|
||||
func Raw(sql string) UnknownExpression {
|
||||
return &expression{sql: sql, priority: 99}
|
||||
}
|
||||
|
||||
func And(expressions ...BooleanExpression) (result BooleanExpression) {
|
||||
if len(expressions) == 0 {
|
||||
result = &expression{sql: "TRUE"}
|
||||
return
|
||||
}
|
||||
for i, condition := range expressions {
|
||||
if i == 0 {
|
||||
result = condition
|
||||
} else {
|
||||
result = result.And(condition)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Or(expressions ...BooleanExpression) (result BooleanExpression) {
|
||||
if len(expressions) == 0 {
|
||||
result = &expression{sql: "FALSE"}
|
||||
return
|
||||
}
|
||||
for i, condition := range expressions {
|
||||
if i == 0 {
|
||||
result = condition
|
||||
} else {
|
||||
result = result.Or(condition)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Function(name string, args ...Expression) Expression {
|
||||
return &expression{sql: name + "(" + commaExpressions(args) + ")"}
|
||||
}
|
||||
|
||||
func If(predicate Expression, trueValue Expression, falseValue Expression) (result Expression) {
|
||||
return Function("IF", predicate, trueValue, falseValue)
|
||||
}
|
||||
|
||||
func commaFields(fields []Field) string {
|
||||
sql := ""
|
||||
for i, item := range fields {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
sql += item.GetSQL()
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func commaExpressions(expressions []Expression) string {
|
||||
sql := ""
|
||||
for i, item := range expressions {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
sql += item.GetSQL()
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func commaValues(values []interface{}) string {
|
||||
sql := ""
|
||||
for i, item := range values {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
value, _ := getSQLFromWhatever(item)
|
||||
sql += value
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func commaAssignments(assignments []assignment) string {
|
||||
sql := ""
|
||||
for i, item := range assignments {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
sql += item.GetSQL()
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func commaOrderBys(orderBys []OrderBy) string {
|
||||
sql := ""
|
||||
for i, item := range orderBys {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
sql += item.GetSQL()
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
func getSQLForName(name string) string {
|
||||
// TODO: check reserved words
|
||||
return "`" + name + "`"
|
||||
}
|
||||
|
||||
func getCallerInfo() string {
|
||||
for i := 0; true; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
segs := strings.Split(file, "/")
|
||||
name := segs[len(segs)-1]
|
||||
switch name {
|
||||
case "common.go", "select.go", "insert.go", "update.go", "delete.go":
|
||||
continue
|
||||
default:
|
||||
return fmt.Sprintf("/* %s:%d */ ", name, line)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Cursor interface {
|
||||
Next() bool
|
||||
Scan(dest ...interface{}) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type cursor struct {
|
||||
rows *sql.Rows
|
||||
}
|
||||
|
||||
func (c *cursor) Next() bool {
|
||||
return c.rows.Next()
|
||||
}
|
||||
|
||||
func preparePointers(val reflect.Value, scans *[]interface{}) error {
|
||||
kind := val.Kind()
|
||||
switch kind {
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
*scans = append(*scans, val.Addr().Interface())
|
||||
case reflect.Slice:
|
||||
case reflect.Struct:
|
||||
for j := 0; j < val.NumField(); j++ {
|
||||
field := val.Field(j)
|
||||
if field.Kind() == reflect.Interface {
|
||||
continue
|
||||
}
|
||||
*scans = append(*scans, field.Addr().Interface())
|
||||
}
|
||||
case reflect.Ptr:
|
||||
toType := val.Type().Elem()
|
||||
switch toType.Kind() {
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
*scans = append(*scans, val.Addr().Interface())
|
||||
default:
|
||||
to := reflect.New(toType).Elem()
|
||||
val.Set(to.Addr())
|
||||
preparePointers(to, scans)
|
||||
}
|
||||
default:
|
||||
println("unknown type %s", kind.String())
|
||||
return fmt.Errorf("unknown type %s", kind.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cursor) Scan(dest ...interface{}) error {
|
||||
|
||||
var scans []interface{}
|
||||
for i, item := range dest {
|
||||
if reflect.ValueOf(item).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("argument %d is not pointer", i)
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(item))
|
||||
|
||||
preparePointers(val, &scans)
|
||||
}
|
||||
|
||||
err := c.rows.Scan(scans...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *cursor) Close() error {
|
||||
return c.rows.Close()
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
db *sql.DB
|
||||
debugMode bool
|
||||
dialect string
|
||||
}
|
||||
|
||||
func (d *Database) SetDebugMode(debugMode bool) {
|
||||
d.debugMode = debugMode
|
||||
}
|
||||
|
||||
func Open(driverName string, dataSourceName string) (db *Database, err error) {
|
||||
var sqlDB *sql.DB
|
||||
if dataSourceName != "" {
|
||||
sqlDB, err = sql.Open(driverName, dataSourceName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
db = &Database{
|
||||
dialect: driverName,
|
||||
db: sqlDB,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Database) GetDB() *sql.DB {
|
||||
return d.db
|
||||
}
|
||||
|
||||
func (d *Database) Query(sql string) (Cursor, error) {
|
||||
startTime := time.Now().UnixNano()
|
||||
rows, err := d.db.Query(sql)
|
||||
endTime := time.Now().UnixNano()
|
||||
printLog(endTime-startTime, sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cursor{rows: rows}, nil
|
||||
}
|
||||
|
||||
func (d *Database) Execute(sql string) (sql.Result, error) {
|
||||
startTime := time.Now().UnixNano()
|
||||
result, err := d.db.Exec(sql)
|
||||
endTime := time.Now().UnixNano()
|
||||
printLog(endTime-startTime, sql)
|
||||
return result, err
|
||||
}
|
||||
|
||||
var printer = message.NewPrinter(language.English)
|
||||
|
||||
func printLog(duration int64, sql string) {
|
||||
printer.Printf("[%9d µs] %s\n", duration/1000, sql)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DeleteWithTable interface {
|
||||
Where(conditions ...BooleanExpression) DeleteWithWhere
|
||||
}
|
||||
|
||||
type DeleteWithWhere interface {
|
||||
GetSQL() (string, error)
|
||||
Execute() (result sql.Result, err error)
|
||||
}
|
||||
|
||||
type deleteStatus struct {
|
||||
database *Database
|
||||
table *Table
|
||||
where *BooleanExpression
|
||||
}
|
||||
|
||||
func (s *deleteStatus) copy() *deleteStatus {
|
||||
delete_ := *s
|
||||
return &delete_
|
||||
}
|
||||
|
||||
func (d *Database) DeleteFrom(table Table) DeleteWithTable {
|
||||
return &deleteStatus{database: d, table: &table}
|
||||
}
|
||||
|
||||
func (s *deleteStatus) Where(conditions ...BooleanExpression) DeleteWithWhere {
|
||||
delete_ := s.copy()
|
||||
condition := And(conditions...)
|
||||
delete_.where = &condition
|
||||
return delete_
|
||||
}
|
||||
|
||||
func (s *deleteStatus) GetSQL() (string, error) {
|
||||
sqlString := getCallerInfo() + "DELETE FROM " + (*s.table).GetSQL() + " WHERE " + (*s.where).GetSQL()
|
||||
|
||||
|
||||
|
||||
return sqlString, nil
|
||||
}
|
||||
|
||||
func (s *deleteStatus) Execute() (sql.Result, error) {
|
||||
sqlString, err := s.GetSQL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.database.Execute(sqlString)
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Expression interface {
|
||||
GetSQL() string
|
||||
getOperatorPriority() int
|
||||
|
||||
NotEquals(other interface{}) BooleanExpression
|
||||
Equals(other interface{}) BooleanExpression
|
||||
LessThan(other interface{}) BooleanExpression
|
||||
LessThanOrEquals(other interface{}) BooleanExpression
|
||||
GreaterThan(other interface{}) BooleanExpression
|
||||
GreaterThanOrEquals(other interface{}) BooleanExpression
|
||||
|
||||
IsNull() BooleanExpression
|
||||
IsNotNull() BooleanExpression
|
||||
In(values ...interface{}) BooleanExpression
|
||||
Between(min interface{}, max interface{}) BooleanExpression
|
||||
Desc() OrderBy
|
||||
|
||||
As(alias string) Alias
|
||||
}
|
||||
|
||||
type BooleanExpression interface {
|
||||
Expression
|
||||
And(other interface{}) BooleanExpression
|
||||
Or(other interface{}) BooleanExpression
|
||||
Not() BooleanExpression
|
||||
}
|
||||
|
||||
type NumberExpression interface {
|
||||
Expression
|
||||
Add(other interface{}) NumberExpression
|
||||
Sub(other interface{}) NumberExpression
|
||||
Mul(other interface{}) NumberExpression
|
||||
Div(other interface{}) NumberExpression
|
||||
IntDiv(other interface{}) NumberExpression
|
||||
Mod(other interface{}) NumberExpression
|
||||
|
||||
Sum() NumberExpression
|
||||
}
|
||||
|
||||
type StringExpression interface {
|
||||
Expression
|
||||
}
|
||||
|
||||
type UnknownExpression interface {
|
||||
Expression
|
||||
And(other interface{}) BooleanExpression
|
||||
Or(other interface{}) BooleanExpression
|
||||
Not() BooleanExpression
|
||||
Add(other interface{}) NumberExpression
|
||||
Sub(other interface{}) NumberExpression
|
||||
Mul(other interface{}) NumberExpression
|
||||
Div(other interface{}) NumberExpression
|
||||
IntDiv(other interface{}) NumberExpression
|
||||
Mod(other interface{}) NumberExpression
|
||||
|
||||
Sum() NumberExpression
|
||||
}
|
||||
|
||||
type expression struct {
|
||||
sql string
|
||||
priority int
|
||||
}
|
||||
|
||||
func (e *expression) As(name string) Alias {
|
||||
return &alias{expression: e, name: name}
|
||||
}
|
||||
|
||||
func (e *expression) GetSQL() string {
|
||||
return e.sql
|
||||
}
|
||||
|
||||
func getSQLFromWhatever(value interface{}) (sql string, priority int) {
|
||||
switch value.(type) {
|
||||
case Expression:
|
||||
return value.(Expression).GetSQL(), value.(Expression).getOperatorPriority()
|
||||
case Assignment:
|
||||
return value.(Assignment).GetSQL(), 0
|
||||
case int, int8, int16, int32, int64:
|
||||
return strconv.FormatInt(reflect.ValueOf(value).Int(), 10), 0
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return strconv.FormatUint(reflect.ValueOf(value).Uint(), 10), 0
|
||||
case string:
|
||||
return "\"" + strings.Replace(value.(string), "\"", "\\\"", -1) + "\"", 0
|
||||
case []interface{}:
|
||||
return "(" + commaValues(value.([]interface{})) + ")", 0
|
||||
default:
|
||||
if value == nil {
|
||||
return "NULL", 0
|
||||
}
|
||||
v := reflect.ValueOf(value)
|
||||
for v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return "NULL", 0
|
||||
}
|
||||
return getSQLFromWhatever(reflect.Indirect(v).Interface())
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return getSQLFromWhatever(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return getSQLFromWhatever(v.Uint())
|
||||
default:
|
||||
return "[invalid type " + v.Kind().String() + "]", 99
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
1 INTERVAL
|
||||
2 BINARY, COLLATE
|
||||
3 !
|
||||
4 - (unary minus), ~ (unary bit inversion)
|
||||
5 ^
|
||||
6 *, /, DIV, %, MOD
|
||||
7 -, +
|
||||
8 <<, >>
|
||||
9 &
|
||||
10 |
|
||||
11 = (comparison), <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
|
||||
12 BETWEEN, CASE, WHEN, THEN, ELSE
|
||||
13 NOT
|
||||
14 AND, &&
|
||||
15 XOR
|
||||
16 OR, ||
|
||||
17 = (assignment), :=
|
||||
*/
|
||||
func (e *expression) NotEquals(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("<>", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) Equals(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("=", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) LessThan(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("<", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) LessThanOrEquals(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("<=", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) GreaterThan(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation(">", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) GreaterThanOrEquals(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation(">=", other, 11)
|
||||
}
|
||||
|
||||
func (e *expression) And(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("AND", other, 14)
|
||||
}
|
||||
|
||||
func (e *expression) Or(other interface{}) BooleanExpression {
|
||||
return e.binaryOperation("OR", other, 16)
|
||||
}
|
||||
|
||||
func (e *expression) Add(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("+", other, 7)
|
||||
}
|
||||
|
||||
func (e *expression) Sub(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("-", other, 7)
|
||||
}
|
||||
|
||||
func (e *expression) Mul(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("*", other, 6)
|
||||
}
|
||||
|
||||
func (e *expression) Div(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("/", other, 6)
|
||||
}
|
||||
|
||||
func (e *expression) IntDiv(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("DIV", other, 6)
|
||||
}
|
||||
|
||||
func (e *expression) Mod(other interface{}) NumberExpression {
|
||||
return e.binaryOperation("%", other, 6)
|
||||
}
|
||||
|
||||
func (e *expression) Sum() NumberExpression {
|
||||
return e.function("SUM")
|
||||
}
|
||||
|
||||
func (e *expression) binaryOperation(operator string, value interface{}, priority int) *expression {
|
||||
left := e.GetSQL()
|
||||
leftLevel := e.priority
|
||||
right, rightLevel := getSQLFromWhatever(value)
|
||||
if leftLevel > priority {
|
||||
left = "(" + left + ")"
|
||||
}
|
||||
if rightLevel >= priority {
|
||||
right = "(" + right + ")"
|
||||
}
|
||||
return &expression{
|
||||
sql: left + " " + operator + " " + right,
|
||||
priority: priority,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *expression) function(name string) *expression {
|
||||
return &expression{sql: name + "(" + e.GetSQL() + ")", priority: 0}
|
||||
}
|
||||
|
||||
func (e *expression) IsNull() BooleanExpression {
|
||||
return &expression{sql: e.GetSQL() + " IS NULL", priority: 11}
|
||||
}
|
||||
|
||||
func (e *expression) Not() BooleanExpression {
|
||||
return &expression{sql: "NOT " + e.GetSQL(), priority: 13}
|
||||
}
|
||||
|
||||
func (e *expression) IsNotNull() BooleanExpression {
|
||||
return &expression{sql: e.GetSQL() + " IS NOT NULL", priority: 11}
|
||||
}
|
||||
|
||||
func (e *expression) In(values ...interface{}) BooleanExpression {
|
||||
if len(values) == 1 {
|
||||
firstValue := values[0]
|
||||
valueOfFirstValue := reflect.ValueOf(firstValue)
|
||||
if valueOfFirstValue.Kind() == reflect.Slice {
|
||||
length := valueOfFirstValue.Len()
|
||||
values = make([]interface{}, length)
|
||||
for i := 0; i < length; i++ {
|
||||
value := valueOfFirstValue.Index(i)
|
||||
values[i] = value.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return &expression{sql: "FALSE", priority: 0}
|
||||
}
|
||||
|
||||
sql := e.GetSQL() + " IN ("
|
||||
for i, value := range values {
|
||||
if i > 0 {
|
||||
sql += ", "
|
||||
}
|
||||
valueSql, _ := getSQLFromWhatever(value)
|
||||
sql += valueSql
|
||||
}
|
||||
sql += ")"
|
||||
return &expression{sql: sql, priority: 11}
|
||||
}
|
||||
|
||||
func (e *expression) Between(min interface{}, max interface{}) BooleanExpression {
|
||||
minSql, _ := getSQLFromWhatever(min)
|
||||
maxSql, _ := getSQLFromWhatever(max)
|
||||
sql := e.GetSQL() + " BETWEEN " + minSql + " AND " + maxSql
|
||||
return &expression{sql: sql, priority: 12}
|
||||
}
|
||||
|
||||
func (e *expression) getOperatorPriority() int {
|
||||
return e.priority
|
||||
}
|
||||
|
||||
func (e *expression) Desc() OrderBy {
|
||||
return &orderBy{by: e, desc: true}
|
||||
}
|
||||
|
||||
func NewExpression(sql string, priority int) {
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package sqlingo
|
||||
|
||||
type Field interface {
|
||||
Expression
|
||||
}
|
||||
|
||||
type NumberField interface {
|
||||
NumberExpression
|
||||
}
|
||||
|
||||
type BooleanField interface {
|
||||
BooleanExpression
|
||||
}
|
||||
|
||||
type StringField interface {
|
||||
StringExpression
|
||||
}
|
||||
|
||||
func newFieldExpression(tableName string, fieldName string) *expression {
|
||||
sql := getSQLForName(fieldName)
|
||||
if tableName != "" {
|
||||
sql = getSQLForName(tableName) + "." + sql
|
||||
}
|
||||
return &expression{sql: sql, priority: 0}
|
||||
}
|
||||
|
||||
func NewNumberField(tableName string, fieldName string) NumberField {
|
||||
return newFieldExpression(tableName, fieldName)
|
||||
}
|
||||
|
||||
func NewBooleanField(tableName string, fieldName string) BooleanField {
|
||||
return newFieldExpression(tableName, fieldName)
|
||||
}
|
||||
|
||||
func NewStringField(tableName string, fieldName string) StringField {
|
||||
return newFieldExpression(tableName, fieldName)
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"go/format"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func convertCase(s string) (result string) {
|
||||
nextCharShouldBeUpperCase := true
|
||||
for _, ch := range s {
|
||||
if ch == '_' {
|
||||
nextCharShouldBeUpperCase = true
|
||||
} else {
|
||||
if nextCharShouldBeUpperCase {
|
||||
result += string(unicode.ToUpper(ch))
|
||||
nextCharShouldBeUpperCase = false
|
||||
} else {
|
||||
result += string(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getType(s string, nullable bool) (goType string, fieldClass string, err error) {
|
||||
r, _ := regexp.Compile("([a-z]+)(\\(([0-9]+)\\))?")
|
||||
|
||||
submatches := r.FindStringSubmatch(s)
|
||||
fieldType := submatches[1]
|
||||
fieldSize := 0
|
||||
if submatches[3] != "" {
|
||||
fieldSize, err = strconv.Atoi(submatches[3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
switch fieldType {
|
||||
case "tinyint":
|
||||
goType = "int8"
|
||||
fieldClass = "NumberField"
|
||||
case "smallint":
|
||||
goType = "int16"
|
||||
fieldClass = "NumberField"
|
||||
case "int":
|
||||
goType = "int32"
|
||||
fieldClass = "NumberField"
|
||||
case "bigint":
|
||||
goType = "int64"
|
||||
fieldClass = "NumberField"
|
||||
case "float", "double":
|
||||
goType = "float64"
|
||||
fieldClass = "NumberField"
|
||||
case "char", "varchar", "text", "mediumtext", "longtext", "enum", "datetime":
|
||||
goType = "string"
|
||||
fieldClass = "StringField"
|
||||
case "blob":
|
||||
goType = "[]byte"
|
||||
fieldClass = "StringField"
|
||||
case "bit":
|
||||
if fieldSize == 1 {
|
||||
goType = "bool"
|
||||
fieldClass = "BooleanField"
|
||||
} else {
|
||||
goType = "string"
|
||||
fieldClass = "StringField"
|
||||
}
|
||||
}
|
||||
if nullable {
|
||||
goType = "*" + goType
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func wrapQuote(s string) string {
|
||||
return "\"" + s + "\""
|
||||
}
|
||||
|
||||
func Generate(driverName string, dataSourceName string, tableNames *[]string) (string, error) {
|
||||
|
||||
mysql, err := sql.Open(driverName, dataSourceName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rows, err := mysql.Query("SELECT DATABASE()")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var dbName *string
|
||||
if rows.Next() {
|
||||
err := rows.Scan(&dbName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if dbName == nil {
|
||||
return "", errors.New("no database selected")
|
||||
}
|
||||
|
||||
code := "package " + *dbName + "_dsl\n"
|
||||
code += "import . \"github.com/lqs/sqlingo\"\n"
|
||||
|
||||
if tableNames == nil {
|
||||
tableNames = &[]string{}
|
||||
rows, err := mysql.Query("SHOW TABLES")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for rows.Next() {
|
||||
var name string
|
||||
rows.Scan(&name)
|
||||
*tableNames = append(*tableNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range *tableNames {
|
||||
rows, err := mysql.Query("SHOW FULL COLUMNS FROM " + getSQLForName(tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tableLines := ""
|
||||
modelLines := ""
|
||||
objectLines := "\tTable: NewTable(\"" + tableName + "\"),\n"
|
||||
classLines := ""
|
||||
|
||||
className := convertCase(tableName)
|
||||
tableStructName := "t" + className
|
||||
|
||||
modelClassName := className + "Model"
|
||||
|
||||
fields := ""
|
||||
values := ""
|
||||
|
||||
for rows.Next() {
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var pointers []interface{}
|
||||
for i := 0; i < len(columns); i++ {
|
||||
var value *string
|
||||
pointers = append(pointers, &value)
|
||||
}
|
||||
err = rows.Scan(pointers...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
row := make(map[string]string)
|
||||
for i, column := range columns {
|
||||
pointer := *pointers[i].(**string)
|
||||
if pointer != nil {
|
||||
row[column] = *pointer
|
||||
}
|
||||
}
|
||||
|
||||
goName := convertCase(row["Field"])
|
||||
goType, fieldClass, _ := getType(row["Type"], row["Null"] == "YES")
|
||||
|
||||
fieldStructName := "f" + className + goName
|
||||
|
||||
tableLines += "\t" + goName + " *" + fieldStructName + "\n"
|
||||
modelLines += "\t" + goName + " " + goType + "\n"
|
||||
objectLines += "\t" + goName + ": &" + fieldStructName + "{"
|
||||
objectLines += "New" + fieldClass + "(" + wrapQuote(tableName) + ", " + wrapQuote(row["Field"]) + ")},\n"
|
||||
classLines += "type " + fieldStructName + " struct{ " + fieldClass + " }\n"
|
||||
|
||||
fields += "t." + goName + ", "
|
||||
values += "m." + goName + ", "
|
||||
}
|
||||
|
||||
//println(tableLines, recordLines, objectLines, classLines)
|
||||
|
||||
code += "type " + tableStructName + " struct {\n\tTable\n"
|
||||
code += tableLines
|
||||
code += "}\n\n"
|
||||
|
||||
code += classLines
|
||||
|
||||
code += "var " + className + " = &" + tableStructName + "{\n"
|
||||
code += objectLines
|
||||
code += "}\n\n"
|
||||
|
||||
code += "func (t t" + className + ") GetFields() []Field {\n"
|
||||
code += "\treturn []Field{" + fields + "}\n"
|
||||
code += "}\n\n"
|
||||
|
||||
code += "type " + modelClassName + " struct {\n"
|
||||
//code += "\tModel\n"
|
||||
code += modelLines
|
||||
code += "}\n\n"
|
||||
|
||||
code += "func (m " + modelClassName + ") GetTable() Table {\n"
|
||||
code += "\treturn " + className + "\n"
|
||||
code += "}\n\n"
|
||||
|
||||
code += "func (m " + modelClassName + ") GetValues() []interface{} {\n"
|
||||
code += "\treturn []interface{}{" + values + "}\n"
|
||||
code += "}\n\n"
|
||||
}
|
||||
|
||||
codeOut, err := format.Source([]byte(code))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(codeOut), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type insertStatus struct {
|
||||
database *Database
|
||||
table *Table
|
||||
fields []Field
|
||||
values []interface{}
|
||||
models []Model
|
||||
onDuplicateKeyUpdateAssignments []assignment
|
||||
}
|
||||
|
||||
func (s *insertStatus) copy() *insertStatus {
|
||||
insert := *s
|
||||
s.fields = append([]Field{}, s.fields...)
|
||||
s.values = append([]interface{}{}, s.values...)
|
||||
s.onDuplicateKeyUpdateAssignments = append([]assignment{}, s.onDuplicateKeyUpdateAssignments...)
|
||||
return &insert
|
||||
}
|
||||
|
||||
type InsertWithTable interface {
|
||||
Fields(fields ... Field) InsertWithValues
|
||||
Values(values ... interface{}) InsertWithValues
|
||||
Models(models ... interface{}) InsertWithModels
|
||||
}
|
||||
|
||||
type InsertWithValues interface {
|
||||
Values(values ... interface{}) InsertWithValues
|
||||
OnDuplicateKeyUpdate() InsertWithOnDuplicateKeyUpdateBegin
|
||||
Execute() (result sql.Result, err error)
|
||||
}
|
||||
|
||||
type InsertWithModels interface {
|
||||
Models(models ... interface{}) InsertWithModels
|
||||
OnDuplicateKeyUpdate() InsertWithOnDuplicateKeyUpdateBegin
|
||||
GetSQL() (string, error)
|
||||
Execute() (result sql.Result, err error)
|
||||
}
|
||||
|
||||
type InsertWithOnDuplicateKeyUpdateBegin interface {
|
||||
Set(Field Field, value interface{}) InsertWithOnDuplicateKeyUpdate
|
||||
}
|
||||
|
||||
type InsertWithOnDuplicateKeyUpdate interface {
|
||||
Set(Field Field, value interface{}) InsertWithOnDuplicateKeyUpdate
|
||||
GetSQL() (string, error)
|
||||
Execute() (result sql.Result, err error)
|
||||
}
|
||||
|
||||
func (d *Database) InsertInto(table Table) InsertWithTable {
|
||||
return &insertStatus{database: d, table: &table}
|
||||
}
|
||||
|
||||
func (s *insertStatus) Fields(fields ... Field) InsertWithValues {
|
||||
insert := s.copy()
|
||||
insert.fields = fields
|
||||
return insert
|
||||
}
|
||||
|
||||
func (s *insertStatus) Values(values ... interface{}) InsertWithValues {
|
||||
insert := s.copy()
|
||||
insert.values = append(insert.values, values)
|
||||
return insert
|
||||
}
|
||||
|
||||
func (s *insertStatus) addModel(model interface{}) {
|
||||
m0, ok := model.(Model)
|
||||
if ok {
|
||||
s.models = append(s.models, m0)
|
||||
return
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(model)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = reflect.Indirect(value)
|
||||
s.addModel(value.Interface())
|
||||
}
|
||||
if value.Kind() == reflect.Slice {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
elem := value.Index(i)
|
||||
addr := elem.Addr()
|
||||
inter := addr.Interface()
|
||||
s.addModel(inter)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *insertStatus) Models(models ... interface{}) InsertWithModels {
|
||||
if len(models) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
insert := s.copy()
|
||||
for _, model := range models {
|
||||
insert.addModel(&model)
|
||||
}
|
||||
return insert
|
||||
}
|
||||
|
||||
func (s *insertStatus) OnDuplicateKeyUpdate() InsertWithOnDuplicateKeyUpdateBegin {
|
||||
insert := s.copy()
|
||||
return insert
|
||||
}
|
||||
|
||||
func (s *insertStatus) Set(field Field, value interface{}) InsertWithOnDuplicateKeyUpdate {
|
||||
insert := s.copy()
|
||||
insert.onDuplicateKeyUpdateAssignments = append(insert.onDuplicateKeyUpdateAssignments, assignment{
|
||||
field: field,
|
||||
value: value,
|
||||
})
|
||||
return insert
|
||||
}
|
||||
|
||||
func (s *insertStatus) GetSQL() (string, error) {
|
||||
var fields []Field
|
||||
var values []interface{}
|
||||
if len(s.models) > 0 {
|
||||
fields = s.models[0].GetTable().GetFields()
|
||||
for _, model := range s.models {
|
||||
values = append(values, model.GetValues())
|
||||
}
|
||||
} else {
|
||||
fields = s.fields
|
||||
values = s.values
|
||||
}
|
||||
|
||||
sqlString := getCallerInfo() + "INSERT INTO " + (*s.table).GetSQL() + " (" + commaFields(fields) + ") VALUES " + commaValues(values)
|
||||
if len(s.onDuplicateKeyUpdateAssignments) > 0 {
|
||||
sqlString += " ON DUPLICATE KEY UPDATE " + commaAssignments(s.onDuplicateKeyUpdateAssignments)
|
||||
}
|
||||
|
||||
return sqlString, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *insertStatus) Execute() (result sql.Result, err error) {
|
||||
sqlString, err := s.GetSQL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.database.Execute(sqlString)
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package sqlingo
|
||||
|
||||
type OrderBy interface {
|
||||
GetSQL() string
|
||||
}
|
||||
|
||||
type orderBy struct {
|
||||
by Expression
|
||||
desc bool
|
||||
}
|
||||
|
||||
func (o *orderBy) GetSQL() string {
|
||||
sql := o.by.GetSQL()
|
||||
if o.desc {
|
||||
sql += " DESC"
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Select interface {
|
||||
GetFields() []Field
|
||||
GetSQL() string
|
||||
Fetch(out ...interface{}) error
|
||||
FetchCursor() (Cursor, error)
|
||||
}
|
||||
|
||||
type SelectWithFields interface {
|
||||
Select
|
||||
From(tables ...Table) SelectWithTables
|
||||
}
|
||||
|
||||
type SelectWithTables interface {
|
||||
Select
|
||||
SelectOrderBy
|
||||
Where(conditions ...BooleanExpression) SelectWithWhere
|
||||
GroupBy(expressions ...Expression) SelectWithGroupBy
|
||||
}
|
||||
|
||||
type SelectWithWhere interface {
|
||||
Select
|
||||
SelectOrderBy
|
||||
|
||||
GroupBy(expressions ...Expression) SelectWithGroupBy
|
||||
}
|
||||
|
||||
type SelectWithGroupBy interface {
|
||||
Select
|
||||
Having(conditions ...BooleanExpression) SelectWithGroupByHaving
|
||||
OrderBy(orderBys ...OrderBy) SelectWithOrder
|
||||
}
|
||||
|
||||
type SelectWithGroupByHaving interface {
|
||||
SelectWithOrder
|
||||
OrderBy(orderBys ...OrderBy) SelectWithOrder
|
||||
}
|
||||
|
||||
type SelectOrderBy interface {
|
||||
OrderBy(orderBys ...OrderBy) SelectWithOrder
|
||||
}
|
||||
|
||||
type SelectWithOrder interface {
|
||||
Limit(limit int) SelectWithLimit
|
||||
Fetch(out ...interface{}) error
|
||||
FetchCursor() (Cursor, error)
|
||||
}
|
||||
|
||||
type SelectWithLimit interface {
|
||||
Offset(offset int) SelectWithOffset
|
||||
Fetch(out ...interface{}) error
|
||||
FetchCursor() (Cursor, error)
|
||||
}
|
||||
|
||||
type SelectWithOffset interface {
|
||||
Fetch(out ...interface{}) error
|
||||
FetchCursor() (Cursor, error)
|
||||
}
|
||||
|
||||
type selectStatus struct {
|
||||
database *Database
|
||||
fields []Field
|
||||
tables []*Table
|
||||
where *BooleanExpression
|
||||
orderBys []OrderBy
|
||||
groupBys []Expression
|
||||
having *BooleanExpression
|
||||
limit *int
|
||||
offset *int
|
||||
lock string
|
||||
}
|
||||
|
||||
func (s *selectStatus) copy() *selectStatus {
|
||||
select_ := *s
|
||||
select_.fields = s.GetFields()
|
||||
return &select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) GetFields() []Field {
|
||||
var fields []Field
|
||||
fields = append(fields, s.fields...)
|
||||
return fields
|
||||
}
|
||||
|
||||
func (d *Database) Select(fields ... interface{}) SelectWithFields {
|
||||
select_ := &selectStatus{database: d}
|
||||
for _, field := range fields {
|
||||
sql, priority := getSQLFromWhatever(field)
|
||||
expression := &expression{sql: sql, priority: priority}
|
||||
select_.fields = append(select_.fields, expression)
|
||||
}
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) From(tables ...Table) SelectWithTables {
|
||||
select_ := s.copy()
|
||||
for _, table := range tables {
|
||||
select_.tables = append(select_.tables, &table)
|
||||
}
|
||||
return select_
|
||||
}
|
||||
|
||||
func (d *Database) SelectFrom(tables ...Table) SelectWithTables {
|
||||
select_ := selectStatus{database: d}
|
||||
for _, table := range tables {
|
||||
select_.tables = append(select_.tables, &table)
|
||||
fields := table.GetFields()
|
||||
for _, field := range fields {
|
||||
select_.fields = append(select_.fields, field)
|
||||
}
|
||||
}
|
||||
|
||||
return &select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) Where(conditions ...BooleanExpression) SelectWithWhere {
|
||||
select_ := s.copy()
|
||||
condition := And(conditions...)
|
||||
select_.where = &condition
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) GroupBy(expressions ...Expression) SelectWithGroupBy {
|
||||
select_ := s.copy()
|
||||
select_.groupBys = expressions
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) Having(conditions ...BooleanExpression) SelectWithGroupByHaving {
|
||||
select_ := s.copy()
|
||||
condition := And(conditions...)
|
||||
select_.having = &condition
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) OrderBy(orderBys ...OrderBy) SelectWithOrder {
|
||||
select_ := s.copy()
|
||||
select_.orderBys = append(select_.orderBys, orderBys...)
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) Limit(limit int) SelectWithLimit {
|
||||
select_ := s.copy()
|
||||
select_.limit = &limit
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) Offset(offset int) SelectWithOffset {
|
||||
select_ := s.copy()
|
||||
select_.limit = &offset
|
||||
return select_
|
||||
}
|
||||
|
||||
func (s *selectStatus) GetSQL() string {
|
||||
sql := getCallerInfo() + "SELECT " + commaFields(s.fields)
|
||||
|
||||
if len(s.tables) > 0 {
|
||||
var values []interface{}
|
||||
for _, table := range s.tables {
|
||||
values = append(values, table)
|
||||
}
|
||||
sql += " FROM " + commaValues(values)
|
||||
}
|
||||
|
||||
if s.where != nil {
|
||||
sql += " WHERE " + (*s.where).GetSQL()
|
||||
}
|
||||
|
||||
if len(s.groupBys) != 0 {
|
||||
sql += " GROUP BY " + commaExpressions(s.groupBys)
|
||||
|
||||
if s.having != nil {
|
||||
sql += " HAVING " + (*s.having).GetSQL()
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.orderBys) > 0 {
|
||||
sql += " ORDER BY " + commaOrderBys(s.orderBys)
|
||||
}
|
||||
|
||||
if s.limit != nil {
|
||||
sql += " LIMIT " + strconv.Itoa(*s.limit)
|
||||
}
|
||||
|
||||
if s.offset != nil {
|
||||
sql += " OFFSET " + strconv.Itoa(*s.offset)
|
||||
}
|
||||
|
||||
sql += s.lock
|
||||
|
||||
return sql
|
||||
}
|
||||
|
||||
func (s *selectStatus) FetchCursor() (Cursor, error) {
|
||||
sqlString := s.GetSQL()
|
||||
|
||||
cursor, err := s.database.Query(sqlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (s *selectStatus) Fetch(dest ...interface{}) error {
|
||||
if len(dest) == 1 {
|
||||
if reflect.ValueOf(dest[0]).Kind() == reflect.Ptr {
|
||||
val := reflect.Indirect(reflect.ValueOf(dest[0]))
|
||||
if val.Kind() == reflect.Slice {
|
||||
cursor, err := s.FetchCursor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for cursor.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
elem := reflect.New(val.Type().Elem())
|
||||
row := elem.Interface()
|
||||
cursor.Scan(row)
|
||||
val.Set(reflect.Append(val, reflect.Indirect(elem)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor, err := s.FetchCursor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for cursor.Next() {
|
||||
err = cursor.Scan(dest...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lqs/sqlingo"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Printf("Usage: %s username:password@/database\n", os.Args[0])
|
||||
return
|
||||
}
|
||||
|
||||
dataSourceName := os.Args[1]
|
||||
|
||||
code, err := sqlingo.Generate("mysql", dataSourceName, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Print(code)
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package sqlingo
|
||||
|
||||
type Table interface {
|
||||
GetName() string
|
||||
GetSQL() string
|
||||
GetFields() []Field
|
||||
}
|
||||
|
||||
type table struct {
|
||||
Table
|
||||
name string
|
||||
sql string
|
||||
}
|
||||
|
||||
func (t *table) GetName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *table) GetSQL() string {
|
||||
return t.sql
|
||||
}
|
||||
|
||||
func (t *table) getOperatorPriority() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func NewTable(name string) Table {
|
||||
return &table{name: name, sql: getSQLForName(name)}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package sqlingo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type updateStatus struct {
|
||||
database *Database
|
||||
table *Table
|
||||
assignments []assignment
|
||||
where *BooleanExpression
|
||||
}
|
||||
|
||||
func (s *updateStatus) copy() *updateStatus {
|
||||
update := *s
|
||||
update.assignments = append([]assignment{}, s.assignments...)
|
||||
return &update
|
||||
}
|
||||
|
||||
func (d *Database) Update(table Table) UpdateWithTable {
|
||||
return &updateStatus{database: d, table: &table}
|
||||
}
|
||||
|
||||
type UpdateWithTable interface {
|
||||
Set(field Field, value interface{}) UpdateWithSet
|
||||
SetMap(map[Field]interface{}) UpdateWithSet
|
||||
}
|
||||
|
||||
type UpdateWithSet interface {
|
||||
Set(Field Field, value interface{}) UpdateWithSet
|
||||
Where(conditions ...BooleanExpression) UpdateWithWhere
|
||||
}
|
||||
|
||||
type UpdateWithWhere interface {
|
||||
GetSQL() (string, error)
|
||||
Execute() (sql.Result, error)
|
||||
}
|
||||
|
||||
func (s *updateStatus) Set(field Field, value interface{}) UpdateWithSet {
|
||||
update := s.copy()
|
||||
update.assignments = append(update.assignments, assignment{
|
||||
field: field,
|
||||
value: value,
|
||||
})
|
||||
return update
|
||||
}
|
||||
|
||||
func (s *updateStatus) SetMap(values map[Field]interface{}) UpdateWithSet {
|
||||
update := s.copy()
|
||||
for field, value := range values {
|
||||
update.assignments = append(update.assignments, assignment{
|
||||
field: field,
|
||||
value: value,
|
||||
})
|
||||
}
|
||||
return update
|
||||
}
|
||||
|
||||
func (s *updateStatus) Where(conditions ...BooleanExpression) UpdateWithWhere {
|
||||
update := s.copy()
|
||||
condition := And(conditions...)
|
||||
update.where = &condition
|
||||
return update
|
||||
}
|
||||
|
||||
func (s *updateStatus) GetSQL() (string, error) {
|
||||
sqlString := getCallerInfo() + "UPDATE " + (*s.table).GetSQL() +
|
||||
" SET " + commaAssignments(s.assignments) +
|
||||
" WHERE " + (*s.where).GetSQL()
|
||||
|
||||
return sqlString, nil
|
||||
}
|
||||
|
||||
func (s *updateStatus) Execute() (sql.Result, error) {
|
||||
sqlString, err := s.GetSQL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.database.Execute(sqlString)
|
||||
}
|
||||
Loading…
Reference in New Issue