Skip to content

Commit

Permalink
Merge pull request #113 from doug-martin/v8.0.1-rc
Browse files Browse the repository at this point in the history
v8.0.1
  • Loading branch information
doug-martin authored Jul 26, 2019
2 parents f38b0e2 + 251428a commit 4a84b3e
Show file tree
Hide file tree
Showing 20 changed files with 859 additions and 484 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v8.0.1

* [ADDED] Multi table update support for `mysql` and `postgres` [#60](https://github.com/doug-martin/goqu/issues/60)
* [ADDED] `goqu.V` so values can be used on the LHS of expressions [#104](https://github.com/doug-martin/goqu/issues/104)

## v8.0.0

A major change the the API was made in `v8` to seperate concerns between the different SQL statement types.
Expand Down
2 changes: 2 additions & 0 deletions dialect/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.SupportsWithCTE = false
opts.SupportsWithCTERecursive = false

opts.UseFromClauseForMultipleUpdateTables = false

opts.PlaceHolderRune = '?'
opts.IncludePlaceholderNum = false
opts.QuoteRune = '`'
Expand Down
10 changes: 10 additions & 0 deletions dialect/mysql/mysql_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,17 @@ func (mds *mysqlDialectSuite) TestBooleanOperations() {
sql, _, err = ds.Where(col.NotILike(regexp.MustCompile("(a|b)"))).ToSQL()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` NOT REGEXP '(a|b)')")
}

func (mds *mysqlDialectSuite) TestUpdateSQL() {
ds := mds.GetDs("test").Update()
sql, _, err := ds.
Set(goqu.Record{"foo": "bar"}).
From("test_2").
Where(goqu.I("test.id").Eq(goqu.I("test_2.test_id"))).
ToSQL()
mds.NoError(err)
mds.Equal("UPDATE `test`,`test_2` SET `foo`='bar' WHERE (`test`.`id` = `test_2`.`test_id`)", sql)
}

func TestDatasetAdapterSuite(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions dialect/mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,24 @@ func (mt *mysqlTest) TestQuery() {
assert.Len(t, entries, 0)
}

func (mt *mysqlTest) TestQuery_ValueExpressions() {
type wrappedEntry struct {
entry
BoolValue bool `db:"bool_value"`
}
expectedDate, err := time.Parse("2006-01-02 15:04:05", "2015-02-22 19:19:55")
mt.NoError(err)
ds := mt.db.From("entry").Select(goqu.Star(), goqu.V(true).As("bool_value")).Where(goqu.Ex{"int": 1})
var we wrappedEntry
found, err := ds.ScanStruct(&we)
mt.NoError(err)
mt.True(found)
mt.Equal(we, wrappedEntry{
entry{2, 1, 0.100000, "0.100000", expectedDate, false, []byte("0.100000")},
true,
})
}

func (mt *mysqlTest) TestCount() {
t := mt.T()
ds := mt.db.From("entry")
Expand Down
32 changes: 32 additions & 0 deletions dialect/postgres/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,27 @@ func (pt *postgresTest) TestQuery() {
assert.Len(t, entries, 0)
}

func (pt *postgresTest) TestQuery_ValueExpressions() {
type wrappedEntry struct {
entry
BoolValue bool `db:"bool_value"`
}
expectedDate, err := time.Parse(time.RFC3339Nano, "2015-02-22T19:19:55.000000000-00:00")
pt.NoError(err)
ds := pt.db.From("entry").Select(goqu.Star(), goqu.V(true).As("bool_value")).Where(goqu.Ex{"int": 1})
var we wrappedEntry
found, err := ds.ScanStruct(&we)
pt.NoError(err)
pt.True(found)
pt.Equal(1, we.Int)
pt.Equal(0.100000, we.Float)
pt.Equal("0.100000", we.String)
pt.Equal(expectedDate.Unix(), we.Time.Unix())
pt.Equal(false, we.Bool)
pt.Equal([]byte("0.100000"), we.Bytes)
pt.True(we.BoolValue)
}

func (pt *postgresTest) TestCount() {
t := pt.T()
ds := pt.db.From("entry")
Expand Down Expand Up @@ -311,6 +332,17 @@ func (pt *postgresTest) TestUpdate() {
assert.Equal(t, id, e.ID)
}

func (pt *postgresTest) TestUpdateSQL_multipleTables() {
ds := pt.db.Update("test")
updateSQL, _, err := ds.
Set(goqu.Record{"foo": "bar"}).
From("test_2").
Where(goqu.I("test.id").Eq(goqu.I("test_2.test_id"))).
ToSQL()
pt.NoError(err)
pt.Equal(`UPDATE "test" SET "foo"='bar' FROM "test_2" WHERE ("test"."id" = "test_2"."test_id")`, updateSQL)
}

func (pt *postgresTest) TestDelete() {
t := pt.T()
ds := pt.db.From("entry")
Expand Down
1 change: 1 addition & 0 deletions dialect/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.SupportsConflictUpdateWhere = false
opts.SupportsInsertIgnoreSyntax = true
opts.SupportsConflictTarget = false
opts.SupportsMultipleUpdateTables = false
opts.WrapCompoundsInParens = false

opts.PlaceHolderRune = '?'
Expand Down
10 changes: 10 additions & 0 deletions dialect/sqlite3/sqlite3_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ func (sds *sqlite3DialectSuite) TestIdentifiers() {
assert.Equal(t, sql, "SELECT `a`, `a`.`b`.`c`, `c`.`d`, `test` AS `test` FROM `test`")
}

func (sds *sqlite3DialectSuite) TestUpdateSQL_multipleTables() {
ds := sds.GetDs("test").Update()
_, _, err := ds.
Set(goqu.Record{"foo": "bar"}).
From("test_2").
Where(goqu.I("test.id").Eq(goqu.I("test_2.test_id"))).
ToSQL()
sds.EqualError(err, "goqu: sqlite3 dialect does not support multiple tables in UPDATE")
}

func (sds *sqlite3DialectSuite) TestCompoundExpressions() {
t := sds.T()
ds1 := sds.GetDs("test").Select("a")
Expand Down
18 changes: 18 additions & 0 deletions dialect/sqlite3/sqlite3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,24 @@ func (st *sqlite3Suite) TestQuery() {
assert.Len(t, entries, 0)
}

func (st *sqlite3Suite) TestQuery_ValueExpressions() {
type wrappedEntry struct {
entry
BoolValue bool `db:"bool_value"`
}
expectedDate, err := time.Parse("2006-01-02 15:04:05", "2015-02-22 19:19:55")
st.NoError(err)
ds := st.db.From("entry").Select(goqu.Star(), goqu.V(true).As("bool_value")).Where(goqu.Ex{"int": 1})
var we wrappedEntry
found, err := ds.ScanStruct(&we)
st.NoError(err)
st.True(found)
st.Equal(we, wrappedEntry{
entry{2, 1, 0.100000, "0.100000", expectedDate, false, []byte("0.100000")},
true,
})
}

func (st *sqlite3Suite) TestCount() {
t := st.T()
ds := st.db.From("entry")
Expand Down
59 changes: 58 additions & 1 deletion docs/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
* [`T`](#T) - An Identifier that represents a Table. With a Table identifier you can fully qualify columns.
* [`C`](#C) - An Identifier that represents a Column. See the docs for more examples
* [`I`](#I) - An Identifier represents a schema, table, or column or any combination. I parses identifiers seperated by a . character.
* [`L`](#L) - An SQL literal.
* [`L`](#L) - An SQL literal.
* [`V`](#V) - An Value to be used in SQL.
* [`And`](#and) - AND multiple expressions together.
* [`Or`](#or) - OR multiple expressions together.
* [Complex Example] - Complex Example using most of the Expression DSL.
Expand Down Expand Up @@ -201,6 +202,61 @@ SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ('a',
SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ($1, $2, $3) [a, b, c]
```

<a name="V"></a>
**[`V()`](https://godoc.org/github.com/doug-martin/goqu#V)**

Sometimes you may have a value that you want to use directly in SQL.

**NOTE** This is a shorter version of `goqu.L("?", val)`

For example you may want to select a value as a column.

```go
ds := goqu.From("user").Select(
goqu.V(true).As("is_verified"),
goqu.V(1.2).As("version"),
"first_name",
"last_name",
)

sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
```

Output:
```
SELECT TRUE AS "is_verified", 1.2 AS "version", "first_name", "last_name" FROM "user" []
```

You can also use `goqu.V` in where clauses.

```
ds := goqu.From("user").Where(goqu.V(1).Neq(1))
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
```

Output:

```
SELECT * FROM "user" WHERE (1 != 1) []
```

You can also use them in prepared statements.

```
ds := goqu.From("user").Where(goqu.V(1).Neq(1))
sql, args, _ := ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
```

Output:

```
SELECT * FROM "user" WHERE (? != ?) [1, 1]
```


<a name="and"></a>
**[`And()`](https://godoc.org/github.com/doug-martin/goqu#And)**

Expand Down Expand Up @@ -388,3 +444,4 @@ HAVING (AVG("test3"."age") > ?)
ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10]
```


45 changes: 45 additions & 0 deletions docs/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [Set with `goqu.Record`](#set-record)
* [Set with struct](#set-struct)
* [Set with map](#set-map)
* [Multi Table](#from)
* [Where](#where)
* [Order](#order)
* [Limit](#limit)
Expand Down Expand Up @@ -167,6 +168,50 @@ Output:
UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
```

<a name="from"></a>
**[From / Multi Table](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.From)**

`goqu` allows joining multiple tables in a update clause through `From`.

**NOTE** The `sqlite3` adapter does not support a multi table syntax.

`Postgres` Example

```go
dialect := goqu.Dialect("postgres")

ds := dialect.Update("table_one").
Set(goqu.Record{"foo": goqu.I("table_two.bar")}).
From("table_two").
Where(goqu.Ex{"table_one.id": goqu.I("table_two.id")})

sql, _, _ := ds.ToSQL()
fmt.Println(sql)
```

Output:
```sql
UPDATE "table_one" SET "foo"="table_two"."bar" FROM "table_two" WHERE ("table_one"."id" = "table_two"."id")
```

`MySQL` Example

```go
dialect := goqu.Dialect("mysql")

ds := dialect.Update("table_one").
Set(goqu.Record{"foo": goqu.I("table_two.bar")}).
From("table_two").
Where(goqu.Ex{"table_one.id": goqu.I("table_two.id")})

sql, _, _ := ds.ToSQL()
fmt.Println(sql)
```
Output:
```sql
UPDATE `table_one`,`table_two` SET `foo`=`table_two`.`bar` WHERE (`table_one`.`id` = `table_two`.`id`)
```

<a name="where"></a>
**[Where](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Where)**

Expand Down
26 changes: 26 additions & 0 deletions exp/update_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ type (
SetTable(table Expression) UpdateClauses

SetValues() interface{}
HasSetValues() bool
SetSetValues(values interface{}) UpdateClauses

From() ColumnListExpression
HasFrom() bool
SetFrom(tables ColumnListExpression) UpdateClauses

Where() ExpressionList
ClearWhere() UpdateClauses
WhereAppend(expressions ...Expression) UpdateClauses
Expand All @@ -38,6 +43,7 @@ type (
commonTables []CommonTableExpression
table Expression
setValues interface{}
from ColumnListExpression
where ExpressionList
order ColumnListExpression
limit interface{}
Expand All @@ -58,6 +64,7 @@ func (uc *updateClauses) clone() *updateClauses {
commonTables: uc.commonTables,
table: uc.table,
setValues: uc.setValues,
from: uc.from,
where: uc.where,
order: uc.order,
limit: uc.limit,
Expand Down Expand Up @@ -86,12 +93,31 @@ func (uc *updateClauses) SetTable(table Expression) UpdateClauses {
func (uc *updateClauses) SetValues() interface{} {
return uc.setValues
}

func (uc *updateClauses) HasSetValues() bool {
return uc.setValues != nil
}

func (uc *updateClauses) SetSetValues(values interface{}) UpdateClauses {
ret := uc.clone()
ret.setValues = values
return ret
}

func (uc *updateClauses) From() ColumnListExpression {
return uc.from
}

func (uc *updateClauses) HasFrom() bool {
return uc.from != nil && !uc.from.IsEmpty()
}

func (uc *updateClauses) SetFrom(from ColumnListExpression) UpdateClauses {
ret := uc.clone()
ret.from = from
return ret
}

func (uc *updateClauses) Where() ExpressionList {
return uc.where
}
Expand Down
23 changes: 23 additions & 0 deletions exp/update_clauses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,29 @@ func (ucs *updateClausesSuite) TestSetSetValues() {
assert.Equal(t, r2, c2.SetValues())
}

func (ucs *updateClausesSuite) TestFrom() {
t := ucs.T()
c := NewUpdateClauses()
ce := NewColumnListExpression("a", "b")
c2 := c.SetFrom(ce)

assert.Nil(t, c.From())

assert.Equal(t, ce, c2.From())
}

func (ucs *updateClausesSuite) TestSetFrom() {
t := ucs.T()
ce1 := NewColumnListExpression("a", "b")
c := NewUpdateClauses().SetFrom(ce1)
ce2 := NewColumnListExpression("a", "b")
c2 := c.SetFrom(ce2)

assert.Equal(t, ce1, c.From())

assert.Equal(t, ce2, c2.From())
}

func (ucs *updateClausesSuite) TestWhere() {
t := ucs.T()
w := Ex{"a": 1}
Expand Down
6 changes: 6 additions & 0 deletions expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ func Literal(sql string, args ...interface{}) exp.LiteralExpression {
return L(sql, args...)
}

// Create a new SQL value ( alias for goqu.L("?", val) ). The prrimary use case for this would be in selects.
// See examples.
func V(val interface{}) exp.LiteralExpression {
return exp.NewLiteralExpression("?", val)
}

// Creates a new Range to be used with a Between expression
// exp.C("col").Between(exp.Range(1, 10))
func Range(start, end interface{}) exp.RangeVal {
Expand Down
Loading

0 comments on commit 4a84b3e

Please sign in to comment.