mirror of https://github.com/aldy505/bob.git
commit
6402e24423
|
@ -1,26 +0,0 @@
|
||||||
name: Build test
|
|
||||||
|
|
||||||
on: [pull_request, push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-test:
|
|
||||||
name: Build test
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
go-version: [1.15.x, 1.16.x, 1.17.x]
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.go-version }}
|
|
||||||
|
|
||||||
- name: Run go vet
|
|
||||||
run: go vet
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test -v -race -covermode=atomic -failfast
|
|
|
@ -3,21 +3,24 @@ name: Test and coverage
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
check:
|
||||||
|
name: Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.17.x
|
go-version: 1.17.x
|
||||||
|
|
||||||
|
- name: Vet check
|
||||||
|
run: go vet -v
|
||||||
|
|
||||||
- name: Run coverage
|
- name: Run coverage
|
||||||
run: go test -v -race -coverprofile=coverage.out -covermode=atomic -failfast
|
run: go test -v -race -coverprofile=coverage.out -covermode=atomic -failfast
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
|
|
71
README.md
71
README.md
|
@ -1,18 +1,51 @@
|
||||||
# Bob - SQL Query Builder
|
# Bob - SQL Query Builder
|
||||||
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/aldy505/bob.svg)](https://pkg.go.dev/github.com/aldy505/bob) [![Go Report Card](https://goreportcard.com/badge/github.com/aldy505/bob)](https://goreportcard.com/report/github.com/aldy505/bob) ![GitHub](https://img.shields.io/github/license/aldy505/bob) [![CodeFactor](https://www.codefactor.io/repository/github/aldy505/bob/badge)](https://www.codefactor.io/repository/github/aldy505/bob) [![codecov](https://codecov.io/gh/aldy505/bob/branch/master/graph/badge.svg?token=Noeexg5xEJ)](https://codecov.io/gh/aldy505/bob) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/9b78970127c74c1a923533e05f65848d)](https://www.codacy.com/gh/aldy505/bob/dashboard?utm_source=github.com&utm_medium=referral&utm_content=aldy505/bob&utm_campaign=Badge_Grade) [![Build test](https://github.com/aldy505/bob/actions/workflows/build.yml/badge.svg)](https://github.com/aldy505/bob/actions/workflows/build.yml) [![Test and coverage](https://github.com/aldy505/bob/actions/workflows/coverage.yml/badge.svg)](https://github.com/aldy505/bob/actions/workflows/coverage.yml)
|
[![Go Reference](pkg-go-dev-badge)](pkg-go-dev-link)
|
||||||
|
[![Go Report Card](go-report-badge)](go-report-link)
|
||||||
|
![GitHub](license-badge)
|
||||||
|
[![CodeFactor](codefactor-badge)](codefactor-link)
|
||||||
|
[![codecov](codecov-badge)](codecov-link)
|
||||||
|
[![Codacy Badge](codacy-badge)](codacy-link)
|
||||||
|
[![Test and coverage](actions-badge)](actions-link)
|
||||||
|
|
||||||
Think of this as an extension of [Squirrel](https://github.com/Masterminds/squirrel) with functionability like [Knex](https://knexjs.org/). I still use Squirrel for other types of queries (insert, select, and all that), but I needed some SQL builder for create table and some other stuffs. Including database creation & upsert.
|
Bob is an SQL builder library initially made as an extension for [Squirrel](squirrel-url)
|
||||||
|
with functionality like [Knex](knex-url) (from the Node.js world). Squirrel itself
|
||||||
|
doesn't provide other types of queries for creating a table, upsert,
|
||||||
|
and some other things. Bob is meant to fill those gaps.
|
||||||
|
|
||||||
Oh, and of course, heavily inspired by [Bob the Builder](https://en.wikipedia.org/wiki/Bob_the_Builder).
|
The different between Bob and Squirrel is that Bob is solely a query builder.
|
||||||
|
The users have to execute and manage the SQL connection themself.
|
||||||
|
Meaning there are no ExecWith() function implemented on Bob, as you can
|
||||||
|
find it on Squirrel.
|
||||||
|
|
||||||
|
The purpose of an SQL query builder is to prevent any typo or mistypes
|
||||||
|
on the SQL queries. Although also with that reason, Bob might not always
|
||||||
|
have the right query for you, depending on what you are doing with the
|
||||||
|
SQL query. It might sometimes be better for you to write the SQL query
|
||||||
|
yourself, if your problem is specific and needs some micro-tweaks.
|
||||||
|
|
||||||
|
With that being said, I hope you enjoy using Bob and consider starring or
|
||||||
|
reporting any issues regarding the usage of Bob in your projects.
|
||||||
|
|
||||||
|
Oh, and of course, heavily inspired by [Bob the Builder](bob-wikipedia).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/aldy505/bob"
|
import "github.com/aldy505/bob"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
Like any other Go projects when you're using Go modules, just put that
|
||||||
|
text right there on the top of your projects, do `go mod tidy` and
|
||||||
|
you are good to go.
|
||||||
|
|
||||||
It's not ready for large-scale production yet (although I've already using it on one of my projects). But, the API is probably close to how you'd do things on Squirrel.
|
Either way, I'm not 100% confident enough to say that this thing is
|
||||||
|
production ready. But, the way I see it, it's good enough to be used
|
||||||
|
on a production-level applications. In fact, I'm using it on one of my
|
||||||
|
current projects that's getting around 100-200 hits per day.
|
||||||
|
|
||||||
|
If you have any feature request or improvement ideas for the project,
|
||||||
|
please kindly open an issue
|
||||||
|
|
||||||
### Create a table
|
### Create a table
|
||||||
|
|
||||||
|
@ -177,7 +210,7 @@ func main() {
|
||||||
Key("email").
|
Key("email").
|
||||||
Replace("age", 40).
|
Replace("age", 40).
|
||||||
ToSql()
|
ToSql()
|
||||||
|
|
||||||
// One more time, for MSSQL / SQL Server.
|
// One more time, for MSSQL / SQL Server.
|
||||||
sql, args, err = bob.
|
sql, args, err = bob.
|
||||||
Upsert("users", bob.MSSQL).
|
Upsert("users", bob.MSSQL).
|
||||||
|
@ -276,7 +309,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Query(context.Background(), inventoryQuery[i])
|
_, err = db.Query(context.Background(), inventoryQuery[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -299,13 +332,6 @@ func main() {
|
||||||
* `bob.Truncate(tableName)` - Truncate a table (`truncate "users"`)
|
* `bob.Truncate(tableName)` - Truncate a table (`truncate "users"`)
|
||||||
* `bob.Upsert(tableName, dialect)` - UPSERT function (`insert into "users" ("name", "email") values (?, ?) on duplicate key update email = ?`)
|
* `bob.Upsert(tableName, dialect)` - UPSERT function (`insert into "users" ("name", "email") values (?, ?) on duplicate key update email = ?`)
|
||||||
|
|
||||||
### TODO
|
|
||||||
|
|
||||||
Meaning these are some ideas for the future development of Bob.
|
|
||||||
|
|
||||||
* `bob.ExecWith()` - Just like Squirrel's [ExecWith](https://pkg.go.dev/github.com/Masterminds/squirrel?utm_source=godoc#ExecWith)
|
|
||||||
* `bob.Count(tableName, columnName)` - Count query (`select count("active") from "users"`)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions are always welcome! As long as you add a test for your changes.
|
Contributions are always welcome! As long as you add a test for your changes.
|
||||||
|
@ -313,3 +339,20 @@ Contributions are always welcome! As long as you add a test for your changes.
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Bob is licensed under [MIT license](./LICENSE)
|
Bob is licensed under [MIT license](./LICENSE)
|
||||||
|
|
||||||
|
[squirrel-url]: https://github.com/Masterminds/squirrel
|
||||||
|
[knex-url]: https://knexjs.org/
|
||||||
|
[bob-wikipedia]: https://en.wikipedia.org/wiki/Bob_the_Builder
|
||||||
|
[pkg-go-dev-badge]: https://pkg.go.dev/badge/github.com/aldy505/bob.svg
|
||||||
|
[pkg-go-dev-link]: https://pkg.go.dev/github.com/aldy505/bob
|
||||||
|
[go-report-badge]: https://goreportcard.com/badge/github.com/aldy505/bob
|
||||||
|
[go-report-link]: https://goreportcard.com/report/github.com/aldy505/bob
|
||||||
|
[license-badge]: https://img.shields.io/github/license/aldy505/bob
|
||||||
|
[codefactor-link]: https://www.codefactor.io/repository/github/aldy505/bob
|
||||||
|
[codefactor-badge]: https://www.codefactor.io/repository/github/aldy505/bob/badge
|
||||||
|
[codecov-badge]: https://codecov.io/gh/aldy505/bob/branch/master/graph/badge.svg?token=Noeexg5xEJ
|
||||||
|
[codecov-link]: https://codecov.io/gh/aldy505/bob
|
||||||
|
[codacy-badge]: https://app.codacy.com/project/badge/Grade/9b78970127c74c1a923533e05f65848d
|
||||||
|
[codacy-link]: https://www.codacy.com/gh/aldy505/bob/dashboard?utm_source=github.com&utm_medium=referral&utm_content=aldy505/bob&utm_campaign=Badge_Grade
|
||||||
|
[actions-badge]: https://github.com/aldy505/bob/actions/workflows/coverage.yml/badge.svg
|
||||||
|
[actions-link]: https://github.com/aldy505/bob/actions/workflows/coverage.yml
|
||||||
|
|
23
bob.go
23
bob.go
|
@ -1,3 +1,26 @@
|
||||||
|
// Bob is an SQL builder library initially made as an extension for Squirrel
|
||||||
|
// with functionality like Knex (from the Node.js world). Squirrel itself
|
||||||
|
// doesn't provide other types of queries for creating a table, upsert,
|
||||||
|
// and some other things. Bob is meant to fill those gaps.
|
||||||
|
//
|
||||||
|
// The different between Bob and Squirrel is that Bob is solely a query builder.
|
||||||
|
// The users have to execute and manage the SQL connection themself.
|
||||||
|
// Meaning there are no ExecWith() function implemented on Bob, as you can
|
||||||
|
// find it on Squirrel.
|
||||||
|
//
|
||||||
|
// The purpose of an SQL query builder is to prevent any typo or mistypes
|
||||||
|
// on the SQL queries. Although also with that reason, Bob might not always
|
||||||
|
// have the right query for you, depending on what you are doing with the
|
||||||
|
// SQL query. It might sometimes be better for you to write the SQL query
|
||||||
|
// yourself, if your problem is specific and needs some micro-tweaks.
|
||||||
|
//
|
||||||
|
// With that being said, I hope you enjoy using Bob and consider starring or
|
||||||
|
// reporting any issues regarding the usage of Bob in your projects.
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021-present Reinaldy Rafli and Bob collaborators
|
||||||
|
//
|
||||||
package bob
|
package bob
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package bob_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aldy505/bob"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCreateIndex() {
|
||||||
|
sql, _, err := bob.
|
||||||
|
CreateIndex("idx_email").
|
||||||
|
On("users").
|
||||||
|
Unique().
|
||||||
|
Columns(bob.IndexColumn{Name: "email", Collate: "DEFAULT", Extras: []string{"ASC"}}).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(sql)
|
||||||
|
// Output: CREATE UNIQUE INDEX idx_email ON users (email COLLATE DEFAULT ASC);
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleHasTable() {
|
||||||
|
sql, args, err := bob.HasTable("users").ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("sql: %s, args: %v", sql, args)
|
||||||
|
// Output: sql: SELECT * FROM information_schema.tables WHERE table_name = ? AND table_schema = current_schema();, args: [users]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleHasColumn() {
|
||||||
|
sql, args, err := bob.HasColumn("email").HasTable("users").ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("sql: %s, args: %v", sql, args)
|
||||||
|
// Output: sql: SELECT * FROM information_schema.columns WHERE table_name = ? AND column_name = ? AND table_schema = current_schema();, args: [users email]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDropTable() {
|
||||||
|
sql, _, err := bob.DropTable("users").Cascade().ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(sql)
|
||||||
|
// Output: DROP TABLE "users" CASCADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDropTableIfExists() {
|
||||||
|
sql, _, err := bob.DropTableIfExists("users").ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(sql)
|
||||||
|
// Output: DROP TABLE IF EXISTS "users";
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTruncate() {
|
||||||
|
sql, _, err := bob.Truncate("users").ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(sql)
|
||||||
|
// Output: TRUNCATE "users";
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRenameTable() {
|
||||||
|
sql, _, err := bob.RenameTable("users", "people").ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(sql)
|
||||||
|
// Output: RENAME TABLE "users" TO "people";
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUpsert() {
|
||||||
|
// Example for MYSQL
|
||||||
|
mysql, myArgs, err := bob.
|
||||||
|
// Notice that you should give database dialect on the second params.
|
||||||
|
// Available database dialect are MySQL, PostgreSQL, SQLite, and MSSQL.
|
||||||
|
Upsert("users", bob.MySQL).
|
||||||
|
Columns("name", "email", "age").
|
||||||
|
// You could do multiple Values() call, but I'd suggest to not do it.
|
||||||
|
// Because this is an upsert function, not an insert one.
|
||||||
|
Values("Thomas Mueler", "tmueler@something.com", 25).
|
||||||
|
Replace("age", 25).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another example for PostgreSQL
|
||||||
|
pgsql, pgArgs, err := bob.
|
||||||
|
Upsert("users", bob.PostgreSQL).
|
||||||
|
Columns("name", "email", "age").
|
||||||
|
Values("Billy Urtha", "billu@something.com", 30).
|
||||||
|
Key("email").
|
||||||
|
Replace("age", 40).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// One more time, for MSSQL / SQL Server.
|
||||||
|
mssql, msArgs, err := bob.
|
||||||
|
Upsert("users", bob.MSSQL).
|
||||||
|
Columns("name", "email", "age").
|
||||||
|
Values("George Rust", "georgee@something.com", 19).
|
||||||
|
Key("email", "georgee@something.com").
|
||||||
|
Replace("age", 18).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Handle this error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("MySQL: %s, %v\n", mysql, myArgs)
|
||||||
|
fmt.Printf("PostgreSQL: %s, %v\n", pgsql, pgArgs)
|
||||||
|
fmt.Printf("MSSQL: %s, %v\n", mssql, msArgs)
|
||||||
|
// Output:
|
||||||
|
// MySQL: INSERT INTO "users" ("name", "email", "age") VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE "age" = ?;, [Thomas Mueler tmueler@something.com 25 25]
|
||||||
|
// PostgreSQL: INSERT INTO "users" ("name", "email", "age") VALUES ($1, $2, $3) ON CONFLICT ("email") DO UPDATE SET "age" = $4;, [Billy Urtha billu@something.com 30 40]
|
||||||
|
// MSSQL: IF NOT EXISTS (SELECT * FROM "users" WHERE "email" = @p1) INSERT INTO "users" ("name", "email", "age") VALUES (@p2, @p3, @p4) ELSE UPDATE "users" SET "age" = @p5 WHERE "email" = @p6;, [georgee@something.com George Rust georgee@something.com 19 18 georgee@something.com]
|
||||||
|
}
|
Loading…
Reference in New Issue