Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
vmihailenco committed May 3, 2021
0 parents commit 54490a0
Show file tree
Hide file tree
Showing 199 changed files with 12,277 additions and 0 deletions.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Tasty ORM for Go

bun is a DB-agnostic ORM for `*sql.DB`.

## Installation

```go
go get github.com/uptrace/bun
```

## Quickstart

First you need to create a `*sql.DB`. Here we using a SQLite3 driver.

```go
import _ "github.com/mattn/go-sqlite3"

sqldb, err := sql.Open("sqlite3", ":memory:?cache=shared")
if err != nil {
panic(err)
}
```

And then create a `*bun.DB` on top of it using the corresponding SQLite dialect:

```go
import (
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/sqlitedialect"
)

db := bun.Open(sqldb, sqlitedialect.New())
```

Now you are ready to issue some queries:

```go
type User struct {
ID int64
Name string
}

user := new(User)
err := db.NewSelect().
Model(user).
Where("name != ?", "").
OrderExpr("id ASC").
Limit(100).
Scan(ctx)
```

The code above is equivalent to:

```go
query := "SELECT id, name FROM users AS user WHERE name != '' ORDER BY id ASC LIMIT 100"

rows, err := sqldb.QueryRows(ctx, query)
if err != nil {
panic(err)
}

user := new(User)
if err := db.ScanRows(ctx, rows, user); err != nil {
panic(err)
}
```

For more details, please check [basic](example/basic) example.
25 changes: 25 additions & 0 deletions bun.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package bun

import (
"github.com/uptrace/bun/schema"
"github.com/uptrace/bun/sqlfmt"
)

type (
Safe = sqlfmt.Safe
Ident = sqlfmt.Ident
)

type (
BeforeScanHook = schema.BeforeScanHook
AfterScanHook = schema.AfterScanHook
AfterSelectHook = schema.AfterSelectHook
BeforeInsertHook = schema.BeforeInsertHook
AfterInsertHook = schema.AfterInsertHook
BeforeUpdateHook = schema.BeforeUpdateHook
AfterUpdateHook = schema.AfterUpdateHook
BeforeDeleteHook = schema.BeforeDeleteHook
AfterDeleteHook = schema.AfterDeleteHook
)

type BaseTable struct{}
229 changes: 229 additions & 0 deletions db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package bun

import (
"context"
"database/sql"
"errors"
"reflect"

"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
"github.com/uptrace/bun/sqlfmt"
)

const (
discardUnknownColumns internal.Flag = 1 << iota
)

type config struct{}

type ConfigOption func(cfg *config)

type DB struct {
*sql.DB
dialect schema.Dialect
features feature.Feature
cfg config

queryHooks []QueryHook

fmter sqlfmt.Formatter
flags internal.Flag
}

func Open(sqldb *sql.DB, dialect schema.Dialect, opts ...ConfigOption) *DB {
db := &DB{
DB: sqldb,
dialect: dialect,
features: dialect.Features(),
fmter: sqlfmt.NewFormatter(dialect.Features()),
}

for _, opt := range opts {
opt(&db.cfg)
}

return db
}

func (db *DB) DiscardUnknownColumns() {
db.flags = db.flags.Set(discardUnknownColumns)
}

func (db *DB) NewValues(model interface{}) *ValuesQuery {
return NewValuesQuery(db, model)
}

func (db *DB) NewSelect() *SelectQuery {
return NewSelectQuery(db)
}

func (db *DB) NewInsert() *InsertQuery {
return NewInsertQuery(db)
}

func (db *DB) NewUpdate() *UpdateQuery {
return NewUpdateQuery(db)
}

func (db *DB) NewDelete() *DeleteQuery {
return NewDeleteQuery(db)
}

func (db *DB) NewCreateTable() *CreateTableQuery {
return NewCreateTableQuery(db)
}

func (db *DB) NewDropTable() *DropTableQuery {
return NewDropTableQuery(db)
}

func (db *DB) Dialect() schema.Dialect {
return db.dialect
}

func (db *DB) ScanRows(ctx context.Context, rows *sql.Rows, dest ...interface{}) error {
defer rows.Close()

model, err := newModel(db, dest)
if err != nil {
return err
}

_, err = model.ScanRows(ctx, rows)
return err
}

func (db *DB) AddQueryHook(hook QueryHook) {
db.queryHooks = append(db.queryHooks, hook)
}

func (db *DB) Table(typ reflect.Type) *schema.Table {
return db.dialect.Tables().Get(typ)
}

func (db *DB) WithArg(name string, value interface{}) *DB {
clone := db.clone()
clone.fmter = clone.fmter.WithArg(name, value)
return clone
}

func (db *DB) clone() *DB {
clone := *db

l := len(clone.queryHooks)
clone.queryHooks = clone.queryHooks[:l:l]

return &clone
}

func (db *DB) Formatter() sqlfmt.Formatter {
return db.fmter
}

//------------------------------------------------------------------------------

func (db *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
return db.ExecContext(context.Background(), query, args...)
}

func (db *DB) ExecContext(
ctx context.Context, query string, args ...interface{},
) (sql.Result, error) {
ctx, event := db.beforeQuery(ctx, nil, query, args)
res, err := db.DB.ExecContext(ctx, query, args...)
db.afterQuery(ctx, event, res, err)

return res, err
}

func (db *DB) Query(query string, args ...interface{}) (*sql.Rows, error) {
return db.QueryContext(context.Background(), query, args...)
}

func (db *DB) QueryContext(
ctx context.Context, query string, args ...interface{},
) (*sql.Rows, error) {
ctx, event := db.beforeQuery(ctx, nil, query, args)
rows, err := db.DB.QueryContext(ctx, query, args...)
db.afterQuery(ctx, event, nil, err)

return rows, err
}

func (db *DB) QueryRow(query string, args ...interface{}) *sql.Row {
return db.QueryRowContext(context.Background(), query, args...)
}

func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
ctx, event := db.beforeQuery(ctx, nil, query, args)
row := db.DB.QueryRowContext(ctx, query, args...)
db.afterQuery(ctx, event, nil, row.Err())
return row
}

type Conn struct {
*sql.Conn
}

func (db *DB) Conn(ctx context.Context) (Conn, error) {
conn, err := db.DB.Conn(ctx)
if err != nil {
return Conn{}, err
}
return Conn{Conn: conn}, nil
}

type Stmt struct {
*sql.Stmt
}

func (db *DB) Prepare(query string) (Stmt, error) {
return db.PrepareContext(context.Background(), query)
}

func (db *DB) PrepareContext(ctx context.Context, query string) (Stmt, error) {
stmt, err := db.DB.PrepareContext(ctx, query)
if err != nil {
return Stmt{}, err
}
return Stmt{Stmt: stmt}, nil
}

type Tx struct {
*sql.Tx
}

func (db *DB) Begin() (Tx, error) {
return db.BeginTx(context.Background(), nil)
}

func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {
tx, err := db.DB.BeginTx(ctx, opts)
if err != nil {
return Tx{}, err
}
return Tx{Tx: tx}, nil
}

//------------------------------------------------------------------------------

type Result struct {
r sql.Result
n int
}

func (r Result) RowsAffected() (int64, error) {
if r.r != nil {
return r.r.RowsAffected()
}
return int64(r.n), nil
}

func (r Result) LastInsertId() (int64, error) {
if r.r != nil {
return r.r.LastInsertId()
}
return 0, errors.New("LastInsertId is not available")
}
7 changes: 7 additions & 0 deletions dialect/dialect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dialect

const (
PG string = "pg"
SQLite string = "sqlite"
MySQL string = "mysql"
)
16 changes: 16 additions & 0 deletions dialect/feature/feature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package feature

import "github.com/uptrace/bun/internal"

type Feature = internal.Flag

const DefaultFeatures = Returning | DropTableCascade

const (
Returning Feature = 1 << iota
DefaultPlaceholder
ValuesRow
Backticks
AutoIncrement
DropTableCascade
)
Loading

0 comments on commit 54490a0

Please sign in to comment.