Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
github.com/doug-martin/goqu/v7
__ _ ___ __ _ _ _
/ _` |/ _ \ / _` | | | |
| (_| | (_) | (_| | |_| |
\__, |\___/ \__, |\__,_|
|___/ |_|
goqu
is an expressive SQL builder and executor
If using go modules.
go get -u github.com/doug-martin/goqu/v7
If you are not using go modules...
NOTE You should still be able to use this package if you are using go version >v1.10
but, you will need to drop the version from the package. import "github.com/doug-martin/goqu/v7
-> import "github.com/doug-martin/goqu"
go get -u github.com/doug-martin/goqu
goqu
comes with many features but here are a few of the more notable ones
SELECT * FROM "items" WHERE "id" = ?
-> SELECT * FROM "items" WHERE "id" = 1
)While goqu may support the scanning of rows into structs it is not intended to be used as an ORM if you are looking for common ORM features like associations, or hooks I would recommend looking at some of the great ORM libraries such as:
We tried a few other sql builders but each was a thin wrapper around sql fragments that we found error prone. goqu
was built with the following goals in mind:
Dialects allow goqu the build the correct SQL for each database. There are three dialects that come packaged with goqu
import _ "github.com/doug-martin/goqu/v7/dialect/mysql"
import _ "github.com/doug-martin/goqu/v7/dialect/postgres"
import _ "github.com/doug-martin/goqu/v7/dialect/sqlite3"
NOTE Dialects work like drivers in go where they are not registered until you import the package.
Below are examples for each dialect. Notice how the dialect is imported and then looked up using goqu.Dialect
import (
"fmt"
"github.com/doug-martin/goqu/v7"
// import the dialect
_ "github.com/doug-martin/goqu/v7/dialect/postgres"
)
// look up the dialect
dialect := goqu.Dialect("postgres")
// use dialect.From to get a dataset to build your SQL
ds := dialect.From("test").Where(goqu.Ex{"id": 10})
sql, args, err := ds.ToSQL()
if err != nil{
fmt.Println("An error occurred while generating the SQL", err.Error())
}else{
fmt.Println(sql, args)
}
Output:
SELECT * FROM "test" WHERE "id" = 10 []
import (
"fmt"
"github.com/doug-martin/goqu/v7"
// import the dialect
_ "github.com/doug-martin/goqu/v7/dialect/mysql"
)
// look up the dialect
dialect := goqu.Dialect("mysql")
// use dialect.From to get a dataset to build your SQL
ds := dialect.From("test").Where(goqu.Ex{"id": 10})
sql, args, err := ds.ToSQL()
if err != nil{
fmt.Println("An error occurred while generating the SQL", err.Error())
}else{
fmt.Println(sql, args)
}
Output:
SELECT * FROM `test` WHERE `id` = 10 []
import (
"fmt"
"github.com/doug-martin/goqu/v7"
// import the dialect
_ "github.com/doug-martin/goqu/v7/dialect/sqlite3"
)
// look up the dialect
dialect := goqu.Dialect("sqlite3")
// use dialect.From to get a dataset to build your SQL
ds := dialect.From("test").Where(goqu.Ex{"id": 10})
sql, args, err := ds.ToSQL()
if err != nil{
fmt.Println("An error occurred while generating the SQL", err.Error())
}else{
fmt.Println(sql, args)
}
Output:
SELECT * FROM `test` WHERE `id` = 10 []
A goqu.Dataset
is the most commonly used data structure used in goqu
. A Dataset
can be used to:
dialect
and expressions
a dataset is an expressive SQL buildergoqu.Database
a goqu.Dataset
can be used to:
ScanStruct
- scan into a structScanStructs
- scan into a slice of structsScanVal
- scan into a primitive value or a driver.Valuer
ScanVals
- scan into a slice of primitive values or driver.Valuer
sCount
- count the number of records in a tablePluck
- pluck a column from a tableInsert
- insert records into a tableUpdate
- update records in a tableDelete
- delete records in a tableTo build SQL with a dialect you can use goqu.Dialect
NOTE if you use do not create a goqu.Database
you can only create SQL
import (
"fmt"
"github.com/doug-martin/goqu/v7"
_ "github.com/doug-martin/goqu/v7/dialect/postgres"
)
dialect := goqu.Dialect("postgres")
//interpolated sql
ds := dialect.From("test").Where(goqu.Ex{"id": 10})
sql, args, err := ds.ToSQL()
if err != nil{
fmt.Println("An error occurred while generating the SQL", err.Error())
}else{
fmt.Println(sql, args)
}
//prepared sql
sql, args, err := ds.Prepared(true).ToSQL()
if err != nil{
fmt.Println("An error occurred while generating the SQL", err.Error())
}else{
fmt.Println(sql, args)
}
Output:
SELECT * FROM "test" WHERE "id" = 10 []
SELECT * FROM "test" WHERE "id" = $1 [10]
goqu
provides an idiomatic DSL for generating SQL. Datasets only act as a clause builder (i.e. Where, From, Select), most of these clause methods accept Expressions which are the building blocks for your SQL statement, you can think of them as fragments of SQL.
The entry points for expressions are:
Ex{}
- A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default Ex
will use the equality operator except in cases where the equality operator will not work, see the example below.
sql, _, _ := db.From("items").Where(goqu.Ex{
"col1": "a",
"col2": 1,
"col3": true,
"col4": false,
"col5": nil,
"col6": []string{"a", "b", "c"},
}).ToSQL()
fmt.Println(sql)
Output:
SELECT * FROM "items" WHERE (("col1" = 'a') AND ("col2" = 1) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IS NULL) AND ("col6" IN ('a', 'b', 'c')))
You can also use the Op
map which allows you to create more complex expressions using the map syntax. When using the Op
map the key is the name of the comparison you want to make (e.g. "neq"
, "like"
, "is"
, "in"
), the key is case insensitive.
sql, _, _ := db.From("items").Where(goqu.Ex{
"col1": goqu.Op{"neq": "a"},
"col3": goqu.Op{"isNot": true},
"col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
}).ToSQL()
fmt.Println(sql)
Output:
SELECT * FROM "items" WHERE (("col1" != 'a') AND ("col3" IS NOT TRUE) AND ("col6" NOT IN ('a', 'b', 'c')))
ExOr{}
- A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default ExOr
will use the equality operator except in cases where the equality operator will not work, see the example below.
sql, _, _ := db.From("items").Where(goqu.ExOr{
"col1": "a",
"col2": 1,
"col3": true,
"col4": false,
"col5": nil,
"col6": []string{"a", "b", "c"},
}).ToSQL()
fmt.Println(sql)
Output:
SELECT * FROM "items" WHERE (("col1" = 'a') OR ("col2" = 1) OR ("col3" IS TRUE) OR ("col4" IS FALSE) OR ("col5" IS NULL) OR ("col6" IN ('a', 'b', 'c')))
You can also use the Op
map which allows you to create more complex expressions using the map syntax. When using the Op
map the key is the name of the comparison you want to make (e.g. "neq"
, "like"
, "is"
, "in"
), the key is case insensitive.
sql, _, _ := db.From("items").Where(goqu.ExOr{
"col1": goqu.Op{"neq": "a"},
"col3": goqu.Op{"isNot": true},
"col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
}).ToSQL()
fmt.Println(sql)
Output:
SELECT * FROM "items" WHERE (("col1" != 'a') OR ("col3" IS NOT TRUE) OR ("col6" NOT IN ('a', 'b', 'c')))
S()
- An Identifier that represents a schema. With a schema identifier you can fully qualify tables and columns.
s := goqu.S("my_schema")
// "my_schema"."my_table"
t := s.Table("my_table")
// "my_schema"."my_table"."my_column"
sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
// SELECT "my_schema"."my_table"."my_column" FROM "my_schema"."my_table"
fmt.Println(sql)
T()
- An Identifier that represents a Table. With a Table identifier you can fully qualify columns.
t := s.Table("my_table")
sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
// SELECT "my_table"."my_column" FROM "my_table"
fmt.Println(sql)
// qualify the table with a schema
sql, _, _ := goqu.From(t.Schema("my_schema")).Select(t.Col("my_column").ToSQL()
// SELECT "my_table"."my_column" FROM "my_schema"."my_table"
fmt.Println(sql)
C()
- An Identifier that represents a Column. See the docs for more examples
sql, _, _ := goqu.From("table").Where(goqu.C("col").Eq(10)).ToSQL()
// SELECT * FROM "table" WHERE "col" = 10
fmt.Println(sql)
I()
- An Identifier represents a schema, table, or column or any combination. I
parses identifiers seperated by a .
character.
// with three parts it is assumed you have provided a schema, table and column
goqu.I("my_schema.table.col") == goqu.S("my_schema").Table("table").Col("col")
// with two parts it is assumed you have provided a table and column
goqu.I("table.col") == goqu.T("table").Col("col")
// with a single value it is the same as calling goqu.C
goqu.I("col") == goqu.C("col")
L()
- An SQL literal. You may find yourself in a situation where an IdentifierExpression cannot expression an SQL fragment that your database supports. In that case you can use a LiteralExpression
// manual casting
goqu.L(`"json"::TEXT = "other_json"::text`)
// custom function invocation
goqu.L(`custom_func("a")`)
// postgres JSON access
goqu.L(`"json_col"->>'someField'`).As("some_field")
You can also use placeholders in your literal with a ?
character. goqu
will handle changing it to what the dialect needs (e.g. ?
mysql, $1
postgres, ?
sqlite3).
NOTE If your query is not prepared the placeholders will be properly interpolated.
goqu.L("col IN (?, ?, ?)", "a", "b", "c")
Putting it together
ds := db.From("test").Where(
goqu.L(`("json"::TEXT = "other_json"::TEXT)`),
goqu.L("col IN (?, ?, ?)", "a", "b", "c"),
)
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
sql, args, _ := ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ('a', 'b', 'c') []
-- assuming postgres dialect
SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ($1, $2, $3) [a, b, c]
And()
- You can use the And
function to AND multiple expressions together.
NOTE By default goqu will AND expressions together
ds := goqu.From("test").Where(
goqu.And(
goqu.C("col").Gt(10),
goqu.C("col").Lt(20),
),
)
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
sql, args, _ = ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM "test" WHERE (("col" > 10) AND ("col" < 20)) []
SELECT * FROM "test" WHERE (("col" > ?) AND ("col" < ?)) [10 20]
Or()
- You can use the Or
function to OR multiple expressions together.
ds := goqu.From("test").Where(
goqu.Or(
goqu.C("col").Eq(10),
goqu.C("col").Eq(20),
),
)
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
sql, args, _ = ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM "test" WHERE (("col" = 10) OR ("col" = 20)) []
SELECT * FROM "test" WHERE (("col" = ?) OR ("col" = ?)) [10 20]
You can also use Or
and And
functions in tandem which will give you control not only over how the Expressions are joined together, but also how they are grouped
ds := goqu.From("items").Where(
goqu.Or(
goqu.C("a").Gt(10),
goqu.And(
goqu.C("b").Eq(100),
goqu.C("c").Neq("test"),
),
),
)
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
sql, args, _ = ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM "items" WHERE (("a" > 10) OR (("b" = 100) AND ("c" != 'test'))) []
SELECT * FROM "items" WHERE (("a" > ?) OR (("b" = ?) AND ("c" != ?))) [10 100 test]
You can also use Or with the map syntax
ds := goqu.From("test").Where(
goqu.Or(
// Ex will be anded together
goqu.Ex{
"col1": 1,
"col2": true,
},
goqu.Ex{
"col3": nil,
"col4": "foo",
},
),
)
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
sql, args, _ = ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM "test" WHERE ((("col1" = 1) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = 'foo'))) []
SELECT * FROM "test" WHERE ((("col1" = ?) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = ?))) [1 foo]
Using the Ex map syntax
ds := db.From("test").
Select(goqu.COUNT("*")).
InnerJoin(goqu.I("test2"), goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.id")})).
LeftJoin(goqu.I("test3"), goqu.On(goqu.Ex{"test2.fkey": goqu.I("test3.id")})).
Where(
goqu.Ex{
"test.name": goqu.Op{"like": regexp.MustCompile("^(a|b)")},
"test2.amount": goqu.Op{"isNot": nil},
},
goqu.ExOr{
"test3.id": nil,
"test3.status": []string{"passed", "active", "registered"},
},
).
Order(goqu.I("test.created").Desc().NullsLast()).
GroupBy(goqu.I("test.user_id")).
Having(goqu.AVG("test3.age").Gt(10))
sql, args, _ := ds.ToSQL()
fmt.Println(sql)
sql, args, _ := ds.Prepared(true).ToSQL()
fmt.Println(sql)
Using the Expression syntax
ds := db.From("test").
Select(goqu.COUNT("*")).
InnerJoin(goqu.I("test2"), goqu.On(goqu.I("test.fkey").Eq(goqu.I("test2.id")))).
LeftJoin(goqu.I("test3"), goqu.On(goqu.I("test2.fkey").Eq(goqu.I("test3.id")))).
Where(
goqu.I("test.name").Like(regexp.MustCompile("^(a|b)")),
goqu.I("test2.amount").IsNotNull(),
goqu.Or(
goqu.I("test3.id").IsNull(),
goqu.I("test3.status").In("passed", "active", "registered"),
),
).
Order(goqu.I("test.created").Desc().NullsLast()).
GroupBy(goqu.I("test.user_id")).
Having(goqu.AVG("test3.age").Gt(10))
sql, args, _ := ds.ToSQL()
fmt.Println(sql)
sql, args, _ := ds.Prepared(true).ToSQL()
fmt.Println(sql)
Both examples generate the following SQL
-- interpolated
SELECT COUNT(*)
FROM "test"
INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
WHERE ((("test"."name" ~ '^(a|b)') AND ("test2"."amount" IS NOT NULL)) AND
(("test3"."id" IS NULL) OR ("test3"."status" IN ('passed', 'active', 'registered'))))
GROUP BY "test"."user_id"
HAVING (AVG("test3"."age") > 10)
ORDER BY "test"."created" DESC NULLS LAST []
-- prepared
SELECT COUNT(*)
FROM "test"
INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
WHERE ((("test"."name" ~ ?) AND ("test2"."amount" IS NOT NULL)) AND
(("test3"."id" IS NULL) OR ("test3"."status" IN (?, ?, ?))))
GROUP BY "test"."user_id"
HAVING (AVG("test3"."age") > ?)
ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10]
goqu
also has basic query support through the use of either the Database or the Dataset.
You can also create a goqu.Database
instance to query records.
In the example below notice that we imported the dialect and driver for side effect only.
import (
"database/sql"
"github.com/doug-martin/goqu/v7"
_ "github.com/doug-martin/goqu/v7/dialect/postgres"
_ "github.com/lib/pq"
)
dialect := goqu.Dialect("postgres")
pgDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
if err != nil {
panic(err.Error())
}
db := dialect.DB(pgDb)
// "SELECT COUNT(*) FROM "user";
if count, err := db.From("user").Count(); err != nil {
fmt.Println(err.Error())
}else{
fmt.Printf("User count = %d", count)
}
ScanStructs
- scans rows into a slice of structs
NOTE ScanStructs
will only select the columns that can be scanned in to the structs unless you have explicitly selected certain columns.
type User struct{
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
var users []User
//SELECT "first_name", "last_name" FROM "user";
if err := db.From("user").ScanStructs(&users); err != nil{
panic(err.Error())
}
fmt.Printf("\n%+v", users)
var users []User
//SELECT "first_name" FROM "user";
if err := db.From("user").Select("first_name").ScanStructs(&users); err != nil{
panic(err.Error())
}
fmt.Printf("\n%+v", users)
ScanStruct
- scans a row into a slice a struct, returns false if a row wasnt found
NOTE ScanStruct
will only select the columns that can be scanned in to the struct unless you have explicitly selected certain columns.
type User struct{
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
var user User
// SELECT "first_name", "last_name" FROM "user" LIMIT 1;
found, err := db.From("user").ScanStruct(&user)
if err != nil{
fmt.Println(err.Error())
return
}
if !found {
fmt.Println("No user found")
} else {
fmt.Printf("\nFound user: %+v", user)
}
NOTE Using the goqu.SetColumnRenameFunction
function, you can change the function that's used to rename struct fields when struct tags aren't defined
import "strings"
goqu.SetColumnRenameFunction(strings.ToUpper)
type User struct{
FirstName string
LastName string
}
var user User
//SELECT "FIRSTNAME", "LASTNAME" FROM "user" LIMIT 1;
found, err := db.From("user").ScanStruct(&user)
// ...
ScanVals
- scans a rows of 1 column into a slice of primitive values
var ids []int64
if err := db.From("user").Select("id").ScanVals(&ids); err != nil{
fmt.Println(err.Error())
return
}
fmt.Printf("\n%+v", ids)
ScanVal
- scans a row of 1 column into a primitive value, returns false if a row wasnt found.
Note when using the dataset a LIMIT
of 1 is automatically applied.
var id int64
found, err := db.From("user").Select("id").ScanVal(&id)
if err != nil{
fmt.Println(err.Error())
return
}
if !found{
fmt.Println("No id found")
}else{
fmt.Printf("\nFound id: %d", id)
}
Count
- Returns the count for the current query
count, err := db.From("user").Count()
if err != nil{
fmt.Println(err.Error())
return
}
fmt.Printf("\nCount:= %d", count)
Pluck
- Selects a single column and stores the results into a slice of primitive values
var ids []int64
if err := db.From("user").Pluck(&ids, "id"); err != nil{
fmt.Println(err.Error())
return
}
fmt.Printf("\nIds := %+v", ids)
Insert
- Creates an INSERT
statement and returns a QueryExecutor
to execute the statement
insert := db.From("user").Insert(goqu.Record{
"first_name": "Bob",
"last_name": "Yukon",
"created": time.Now(),
})
if _, err := insert.Exec(); err != nil{
fmt.Println(err.Error())
return
}
Insert will also handle multi inserts if supported by the database
users := []goqu.Record{
{"first_name": "Bob", "last_name": "Yukon", "created": time.Now()},
{"first_name": "Sally", "last_name": "Yukon", "created": time.Now()},
{"first_name": "Jimmy", "last_name": "Yukon", "created": time.Now()},
}
if _, err := db.From("user").Insert(users).Exec(); err != nil{
fmt.Println(err.Error())
return
}
If your database supports the RETURN
clause you can also use the different Scan methods to get results
var ids []int64
users := []goqu.Record{
{"first_name": "Bob", "last_name": "Yukon", "created": time.Now()},
{"first_name": "Sally", "last_name": "Yukon", "created": time.Now()},
{"first_name": "Jimmy", "last_name": "Yukon", "created": time.Now()},
}
if err := db.From("user").Returning(goqu.C("id")).Insert(users).ScanVals(&ids); err != nil{
fmt.Println(err.Error())
return
}
Update
- Creates an UPDATE
statement and returns QueryExecutor
to execute the statement
update := db.From("user").
Where(goqu.C("status").Eq("inactive")).
Update(goqu.Record{"password": nil, "updated": time.Now()})
if _, err := update.Exec(); err != nil{
fmt.Println(err.Error())
return
}
If your database supports the RETURN
clause you can also use the different Scan methods to get results
var ids []int64
update := db.From("user").
Where(goqu.Ex{"status":"inactive"}).
Returning("id").
Update(goqu.Record{"password": nil, "updated": time.Now()})
if err := update.ScanVals(&ids); err != nil{
fmt.Println(err.Error())
return
}
Delete
- Creates an DELETE
statement and returns a QueryExecutor
to execute the statement
delete := db.From("invoice").
Where(goqu.Ex{"status":"paid"}).
Delete()
if _, err := delete.Exec(); err != nil{
fmt.Println(err.Error())
return
}
If your database supports the RETURN
clause you can also use the different Scan methods to get results
var ids []int64
delete := db.From("invoice").
Where(goqu.C("status").Eq("paid")).
Returning(goqu.C("id")).
Delete()
if err := delete.ScanVals(&ids); err != nil{
fmt.Println(err.Error())
return
}
By default the Dataset
will interpolate all parameters, if you do not want to have values interpolated you can use the Prepared
method to prevent this.
Note For the examples all placeholders are ?
this will be dialect specific when using other examples (e.g. Postgres $1, $2...
)
preparedDs := db.From("items").Prepared(true)
sql, args, _ := preparedDs.Where(goqu.Ex{
"col1": "a",
"col2": 1,
"col3": true,
"col4": false,
"col5": []string{"a", "b", "c"},
}).ToSQL()
fmt.Println(sql, args)
sql, args, _ = preparedDs.ToInsertSQL(
goqu.Record{"name": "Test1", "address": "111 Test Addr"},
goqu.Record{"name": "Test2", "address": "112 Test Addr"},
)
fmt.Println(sql, args)
sql, args, _ = preparedDs.ToUpdateSQL(
goqu.Record{"name": "Test", "address": "111 Test Addr"},
)
fmt.Println(sql, args)
sql, args, _ = preparedDs.
Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
ToDeleteSQL()
fmt.Println(sql, args)
// Output:
// SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IN (?, ?, ?))) [a 1 a b c]
// INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
// UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
// DELETE FROM "items" WHERE ("id" > ?) [10]
When setting prepared to true executing the SQL using the different querying methods will also use the non-interpolated SQL also.
var items []Item
sql, args, _ := db.From("items").Prepared(true).Where(goqu.Ex{
"col1": "a",
"col2": 1,
}).ScanStructs(&items)
//Is the same as
db.ScanStructs(&items, `SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?))`, "a", 1)
The Database also allows you to execute queries but expects raw SQL to execute. The supported methods are
goqu
has builtin support for transactions to make the use of the Datasets and querying seamless
tx, err := db.Begin()
if err != nil{
return err
}
//use tx.From to get a dataset that will execute within this transaction
update := tx.From("user").
Where(goqu.Ex("password": nil}).
Update(goqu.Record{"status": "inactive"})
if _, err = update.Exec(); err != nil{
if rErr := tx.Rollback(); rErr != nil{
return rErr
}
return err
}
if err = tx.Commit(); err != nil{
return err
}
return
The TxDatabase
also has all methods that the Database
has along with
The TxDatabase.Wrap
is a convience method for automatically handling COMMIT
and ROLLBACK
tx, err := db.Begin()
if err != nil{
return err
}
err = tx.Wrap(func() error{
update := tx.From("user").
Where(goqu.Ex("password": nil}).
Update(goqu.Record{"status": "inactive"})
return update.Exec()
})
//err will be the original error from the update statement, unless there was an error executing ROLLBACK
if err != nil{
return err
}
To enable trace logging of SQL statements use the Database.Logger
method to set your logger.
NOTE The logger must implement the Logger
interface
NOTE If you start a transaction using a database your set a logger on the transaction will inherit that logger automatically
Dialects in goqu are the foundation of building the correct SQL for each DB dialect.
Most SQL dialects share a majority of their syntax, for this reason goqu
has a default set of dialect options that can be used as a base for any new Dialect.
When creating a new SQLDialect
you just need to override the default values that are documented in SQLDialectOptions
.
Take a look at postgres
, mysql
and sqlite3
for examples.
When creating a new dialect you must register it using RegisterDialect
. This method requires 2 arguments.
dialect string
- The name of your dialectopts SQLDialectOptions
- The custom options for your dialectFor example you could create a custom dialect that replaced the default quote '"'
with a backtick `
opts := goqu.DefaultDialectOptions()
opts.QuoteRune = '`'
goqu.RegisterDialect("custom-dialect", opts)
dialect := goqu.Dialect("custom-dialect")
ds := dialect.From("test")
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
Output:
SELECT * FROM `test` []
For more examples look at postgres
, mysql
and sqlite3
for examples.
<v7 to v7
Sql
to SQL
ToSql
-> ToSQL
ToInsertSql
-> ToInsertSQL
ToUpdateSql
-> ToUpdateSQL
ToDeleteSql
-> ToDeleteSQL
ToTruncateSql
-> ToTruncateSQL
dialect_options
from the adapter to make the dialect self contained.
dataset<->adapter
co dependency making the dialect self contained.goqu.I
method.
goqu.S
, goqu.T
and goqu.C
methods to clarify why type of identifier you are using.goqu.I
should only be used when you have a qualified identifier (e.g. `goqu.I("my_schema.my_table.my_col")goqu.Dialect
method to make using goqu
as an SQL builder easier.I am always welcoming contributions of any type. Please open an issue or create a PR if you find an issue with any of the following.
If you have an issue with the package please include the following
Without those basics it can be difficult to reproduce your issue locally. You may be asked for more information but that is a good starting point.
New features and/or enhancements are great and I encourage you to either submit a PR or create an issue. In both cases include the following as the need/requirement may not be readily apparent.
If you are issuing a PR also also include the following
If you find an issue you want to work on please comment on it letting other people know you are looking at it and I will assign the issue to you.
If want to work on an issue but dont know where to start just leave a comment and I'll be more than happy to point you in the right direction.
The test suite requires a postgres and mysql database. You can override the mysql/postgres connection strings with the MYSQL_URI
and PG_URI
environment variables*
go test -v -race ./...
You can also run the tests in a container using docker-compose.
GO_VERSION=latest docker-compose run goqu
goqu
is released under the MIT License.
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.