• Stars
    star
    174
  • Rank 219,104 (Top 5 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 8 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Additions to Go's database/sql for super fast performance and convenience. (fork of gocraft/dbr)

dbr (fork of gocraft/dbr) provides additions to Go's database/sql for super fast performance and convenience.

Build Status Go Report Card Coverage Status

Getting Started

// create a connection (e.g. "postgres", "mysql", or "sqlite3")
conn, _ := dbr.Open("postgres", "...")

// create a session for each business unit of execution (e.g. a web request or goworkers job)
sess := conn.NewSession(nil)

// get a record
var suggestion Suggestion
sess.Select("id", "title").From("suggestions").Where("id = ?", 1).Load(&suggestion)

// JSON-ready, with dbr.Null* types serialized like you want
json.Marshal(&suggestion)

Feature highlights

Use a Sweet Query Builder or use Plain SQL

mailru/dbr supports both.

Sweet Query Builder:

stmt := dbr.Select("title", "body").
	From("suggestions").
	OrderBy("id").
	Limit(10)

Plain SQL:

builder := dbr.SelectBySql("SELECT `title`, `body` FROM `suggestions` ORDER BY `id` ASC LIMIT 10")

Amazing instrumentation with session

All queries in mailru/dbr are made in the context of a session. This is because when instrumenting your app, it's important to understand which business action the query took place in.

Writing instrumented code is a first-class concern for mailru/dbr. We instrument each query to emit to a EventReceiver interface.

Faster performance than using database/sql directly

Every time you call database/sql's db.Query("SELECT ...") method, under the hood, the mysql driver will create a prepared statement, execute it, and then throw it away. This has a big performance cost.

mailru/dbr doesn't use prepared statements. We ported mysql's query escape functionality directly into our package, which means we interpolate all of those question marks with their arguments before they get to MySQL. The result of this is that it's way faster, and just as secure.

Check out these benchmarks.

IN queries that aren't horrible

Traditionally, database/sql uses prepared statements, which means each argument in an IN clause needs its own question mark. mailru/dbr, on the other hand, handles interpolation itself so that you can easily use a single question mark paired with a dynamically sized slice.

ids := []int64{1, 2, 3, 4, 5}
builder.Where("id IN ?", ids) // `id` IN ?

map object can be used for IN queries as well. Note: interpolation map is slower than slice and it is preferable to use slice when it is possible.

ids := map[int64]string{1: "one", 2: "two"}
builder.Where("id IN ?", ids)  // `id` IN ?

JSON Friendly

Every try to JSON-encode a sql.NullString? You get:

{
	"str1": {
		"Valid": true,
		"String": "Hi!"
	},
	"str2": {
		"Valid": false,
		"String": ""
  }
}

Not quite what you want. mailru/dbr has dbr.NullString (and the rest of the Null* types) that encode correctly, giving you:

{
	"str1": "Hi!",
	"str2": null
}

Inserting multiple records

sess.InsertInto("suggestions").Columns("title", "body").
  Record(suggestion1).
  Record(suggestion2)

Updating records on conflict

stmt := sess.InsertInto("suggestions").Columns("title", "body").Record(suggestion1)
stmt.OnConflict("suggestions_pkey").Action("body", dbr.Proposed("body"))

Updating records

sess.Update("suggestions").
	Set("title", "Gopher").
	Set("body", "I love go.").
	Where("id = ?", 1)

Transactions

tx, err := sess.Begin()
if err != nil {
  return err
}
defer tx.RollbackUnlessCommitted()

// do stuff...

return tx.Commit()

Load database values to variables

Querying is the heart of mailru/dbr.

  • Load(&any): load everything!
  • LoadStruct(&oneStruct): load struct
  • LoadStructs(&manyStructs): load a slice of structs
  • LoadValue(&oneValue): load basic type
  • LoadValues(&manyValues): load a slice of basic types
// columns are mapped by tag then by field
type Suggestion struct {
	ID int64  // id, will be autoloaded by last insert id
	Title string // title
	Url string `db:"-"` // ignored
	secret string // ignored
	Body dbr.NullString `db:"content"` // content
	User User
}

// By default dbr converts CamelCase property names to snake_case column_names
// You can override this with struct tags, just like with JSON tags
// This is especially helpful while migrating from legacy systems
type Suggestion struct {
	Id        int64
	Title     dbr.NullString `db:"subject"` // subjects are called titles now
	CreatedAt dbr.NullTime
}

var suggestions []Suggestion
sess.Select("*").From("suggestions").Load(&suggestions)

Join multiple tables

dbr supports many join types:

sess.Select("*").From("suggestions").
  Join("subdomains", "suggestions.subdomain_id = subdomains.id")

sess.Select("*").From("suggestions").
  LeftJoin("subdomains", "suggestions.subdomain_id = subdomains.id")

sess.Select("*").From("suggestions").
  RightJoin("subdomains", "suggestions.subdomain_id = subdomains.id")

sess.Select("*").From("suggestions").
  FullJoin("subdomains", "suggestions.subdomain_id = subdomains.id")

You can join on multiple tables:

sess.Select("*").From("suggestions").
  Join("subdomains", "suggestions.subdomain_id = subdomains.id").
  Join("accounts", "subdomains.accounts_id = accounts.id")

Quoting/escaping identifiers (e.g. table and column names)

dbr.I("suggestions.id") // `suggestions`.`id`

Subquery

sess.Select("count(id)").From(
  dbr.Select("*").From("suggestions").As("count"),
)

Union

dbr.Union(
  dbr.Select("*"),
  dbr.Select("*"),
)

dbr.UnionAll(
  dbr.Select("*"),
  dbr.Select("*"),
)

Union can be used in subquery.

Alias/AS

  • SelectStmt
dbr.Select("*").From("suggestions").As("count")
  • Identity
dbr.I("suggestions").As("s")
  • Union
dbr.Union(
  dbr.Select("*"),
  dbr.Select("*"),
).As("u1")

dbr.UnionAll(
  dbr.Select("*"),
  dbr.Select("*"),
).As("u2")

Building arbitrary condition

One common reason to use this is to prevent string concatenation in a loop.

  • And
  • Or
  • Eq
  • Neq
  • Gt
  • Gte
  • Lt
  • Lte
dbr.And(
  dbr.Or(
    dbr.Gt("created_at", "2015-09-10"),
    dbr.Lte("created_at", "2015-09-11"),
  ),
  dbr.Eq("title", "hello world"),
)

Built with extensibility

The core of dbr is interpolation, which can expand ? with arbitrary SQL. If you need a feature that is not currently supported, you can build it on your own (or use dbr.Expr).

To do that, the value that you wish to be expaned with ? needs to implement dbr.Builder.

type Builder interface {
	Build(Dialect, Buffer) error
}

Driver support

  • MySQL
  • PostgreSQL
  • SQLite3
  • ClickHouse

These packages were developed by the engineering team at UserVoice and currently power much of its infrastructure and tech stack.

Thanks & Authors

Inspiration from these excellent libraries:

  • sqlx - various useful tools and utils for interacting with database/sql.
  • Squirrel - simple fluent query builder.

Authors:

Contributors:

More Repositories

1

easyjson

Fast JSON serializer for golang.
Go
4,406
star
2

FileAPI

FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
JavaScript
3,579
star
3

easygo

Tools for building go apps.
Go
671
star
4

icqdesktop.deprecated

C++
461
star
5

go-clickhouse

Golang SQL database driver for Yandex ClickHouse
Go
418
star
6

graphite-nginx-module

An nginx module for collecting stats into Graphite
C
135
star
7

fest

javascript templates
JavaScript
128
star
8

jira-scripts

Groovy
113
star
9

tntlua

Tarantool 1.5 Lua stored procedures
Lua
83
star
10

tarantool-authman

Lua
53
star
11

surgemq

Go
19
star
12

jira-plugins-jsincluder

JavaScript
16
star
13

shadowplay

Rust
15
star
14

designsystemsclub

Каталог отечественных компонентных дизайн-систем, реализованных на технологическом уровне. Живые гайдлайны, статьи, презентации, выступления и другие материалы о них.
CSS
15
star
15

jira-plugins-mrimsender

Mail.Ru Agent Notifications JIRA Plugin
Java
13
star
16

sumbur-ruby

sumbur-ruby
Java
12
star
17

ipro-cli

iproto cli tools
C
12
star
18

March

Kotlin
11
star
19

activerecord

Go
10
star
20

confetti

confetti - configuration file parser generator
C
10
star
21

mail-auth-sdk-android

Java
9
star
22

opensource.mail.ru

opensource.mail.ru wiki content
HTML
9
star
23

nocaptcha-php

PHP
8
star
24

imaginelua

Lua
6
star
25

slick-migration

Slick-migration - is a Scala library that helps to maintain compilable migration scripts
Scala
5
star
26

top-mail-ru

PHP
5
star
27

hit-doc

PHP
5
star
28

libzxcvbn

Simple implementation of zxcvbn in C
C
4
star
29

queue-processor

PHP queues processing tool
PHP
4
star
30

inetnums

Python
4
star
31

jira-plugins-commons

Dependency module
Java
4
star
32

nocaptcha-bitrix

CMS Bitrix module for working with Nocaptcha Mail.Ru service
PHP
4
star
33

iproto-ruby

Ruby implementation of Mail.Ru iproto protocol
Ruby
3
star
34

confluence-plugins-utils

Java
3
star
35

mrasender

Tarantool module for sending messages to Mail.Ru Agent
C
3
star
36

nocaptcha-perl

Perl module for working with Nocaptcha Mail.Ru service
Perl
3
star
37

bamboo-plugins-utils

Utils for Bamboo
Java
2
star
38

nocaptcha-python

Python
2
star
39

mail-auth-sdk-ios

Objective-C
2
star
40

VK-Jira-Plugins

1
star
41

quotas

Lua
1
star
42

2kit

1
star
43

skill_cosmo_quest

Python
1
star
44

wdio-redirect-by-pattern-service

TypeScript
1
star
45

tslint-2kit-rules

1
star
46

nocaptcha-wordpress

WordPress module for working with Nocaptcha Mail.Ru service
PHP
1
star
47

clearnet

Kotlin
1
star
48

activerecord-cookbook

Go
1
star