From 251428aa094c9328d05f68f4adee8fe788413c4e Mon Sep 17 00:00:00 2001 From: doug-martin Date: Thu, 25 Jul 2019 19:44:18 -0500 Subject: [PATCH] Added goqu.V for #104 * [ADDED] `goqu.V` so values can be used on the LHS of expressions #104 --- HISTORY.md | 1 + dialect/mysql/mysql_test.go | 18 ++++++++++ dialect/postgres/postgres_test.go | 21 +++++++++++ dialect/sqlite3/sqlite3_test.go | 18 ++++++++++ docs/expressions.md | 59 ++++++++++++++++++++++++++++++- expressions.go | 6 ++++ expressions_example_test.go | 41 +++++++++++++++++++++ sql_dialect.go | 6 ---- 8 files changed, 163 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e53394e1..56ca9c8f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,7 @@ ## 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 diff --git a/dialect/mysql/mysql_test.go b/dialect/mysql/mysql_test.go index 9086453c..ac19ff14 100644 --- a/dialect/mysql/mysql_test.go +++ b/dialect/mysql/mysql_test.go @@ -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") diff --git a/dialect/postgres/postgres_test.go b/dialect/postgres/postgres_test.go index 057f2f21..e57b000a 100644 --- a/dialect/postgres/postgres_test.go +++ b/dialect/postgres/postgres_test.go @@ -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") diff --git a/dialect/sqlite3/sqlite3_test.go b/dialect/sqlite3/sqlite3_test.go index 6976f9e0..9e25108e 100644 --- a/dialect/sqlite3/sqlite3_test.go +++ b/dialect/sqlite3/sqlite3_test.go @@ -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") diff --git a/docs/expressions.md b/docs/expressions.md index a06682f6..945db00a 100644 --- a/docs/expressions.md +++ b/docs/expressions.md @@ -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. @@ -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] ``` + +**[`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] +``` + + **[`And()`](https://godoc.org/github.com/doug-martin/goqu#And)** @@ -388,3 +444,4 @@ HAVING (AVG("test3"."age") > ?) ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10] ``` + diff --git a/expressions.go b/expressions.go index b7a31b76..097efc14 100644 --- a/expressions.go +++ b/expressions.go @@ -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 { diff --git a/expressions_example_test.go b/expressions_example_test.go index 42b53c06..12449574 100644 --- a/expressions_example_test.go +++ b/expressions_example_test.go @@ -1655,6 +1655,47 @@ func ExampleRecord_update() { // UPDATE "test" SET "col1"=?,"col2"=? [1 foo] } +func ExampleV() { + 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) + + ds = goqu.From("user").Where(goqu.V(1).Neq(1)) + sql, args, _ = ds.ToSQL() + fmt.Println(sql, args) + + // Output: + // SELECT TRUE AS "is_verified", 1.2 AS "version", "first_name", "last_name" FROM "user" [] + // SELECT * FROM "user" WHERE (1 != 1) [] +} + +func ExampleV_prepared() { + ds := goqu.From("user").Select( + goqu.V(true).As("is_verified"), + goqu.V(1.2).As("version"), + "first_name", + "last_name", + ) + + sql, args, _ := ds.Prepared(true).ToSQL() + fmt.Println(sql, args) + + ds = goqu.From("user").Where(goqu.V(1).Neq(1)) + + sql, args, _ = ds.Prepared(true).ToSQL() + fmt.Println(sql, args) + + // Output: + // SELECT ? AS "is_verified", ? AS "version", "first_name", "last_name" FROM "user" [true 1.2] + // SELECT * FROM "user" WHERE (? != ?) [1 1] +} + func ExampleVals() { ds := goqu.Insert("user"). Cols("first_name", "last_name", "is_verified"). diff --git a/sql_dialect.go b/sql_dialect.go index 63258de3..5cfee69d 100644 --- a/sql_dialect.go +++ b/sql_dialect.go @@ -726,9 +726,6 @@ func (d *sqlDialect) onConflictSQL(b sb.SQLBuilder, o exp.ConflictExpression) { } func (d *sqlDialect) updateTableSQL(b sb.SQLBuilder, uc exp.UpdateClauses) { - if b.Error() != nil { - return - } b.WriteRunes(d.dialectOptions.SpaceRune) d.Literal(b, uc.Table()) if b.Error() != nil { @@ -758,9 +755,6 @@ func (d *sqlDialect) updateValuesSQL(b sb.SQLBuilder, updates ...exp.UpdateExpre } func (d *sqlDialect) updateFromSQL(b sb.SQLBuilder, ce exp.ColumnListExpression) { - if b.Error() != nil { - return - } if ce == nil || ce.IsEmpty() { return }