Giter Club home page Giter Club logo

sqlkit's Introduction

SQLKit

travis codecov goreport godoc release license

Multipurpose SQL packages for GO programs.

Working with SQL in golang is challenging in certain respects. Some of the main challenges I've encountered have been:

  • The way NULL values are handled by the sql package requires using pointers in places where often just the zero value would be used.
  • The lack of nested transactions which are very valuable when trying to wrap an entire test or work with more complex transactions.
  • Lacking a simple and extendable SQL builder.

This project is designed to fix some of these issues. It is heavily influenced by sqlx https://github.com/jmoiron/sqlx but with more opinions on how SQL should be used in projects.

View the example to see an example of using this project.

Status

This project is currently in an ALPHA state. The api is relatively solid for the encoding package but may change for the db package.

Versioning

This project follows semantic versioning. Best effort will be maintained to keep backwards compatibility as soon as the api stabilizes at 1.0.0.

Packages

Marshal to structs to and from SQL. Gracefully handles null values.

cols, vals, err := encoding.Marshal(user{1})
_, err = db.Exec(
"insert into users ("+strings.Join(cols, ",")+") values "+"(?)", vals...,
)

users := []user{}
rows, err := db.Query("select * from users")
err = encoding.Unmarsal(&users, rows)

Basic SQL query builder for SQL which handles marshalling and unmarshalling values using the sqlkit/encoding package. Features include:

  • Query builder for SQL statements.
  • Nested transactions using savepoints.
  • Support for Postgres, MySQL and other sql flavours.
  • Extensible query logging.
  • Expands placeholders for IN (?) queries.

An example of common API usage:

d, err := db.Open("sqlite3", ":memory:", db.WithLogger(db.StdLogger))

tx, err := d.Begin(context.Background()) // tx implements context.Context.
defer tx.Rollback()

err = d.Exec(tx, d.Insert().Into("test").Value("id", 2)).Err()

var rows []int
err = d.Query(tx, d.Select("*").From("test")).Decode(&rows)
fmt.Printf("%v\n", rows) // Can decode a slice of objects or scalars.

var count int
err = d.Query(tx, d.Select("count(*)").From("test")).Decode(&count)
fmt.Println(count) // Can decode single values.

Overview

Transactions

Transactions are implemented as a wrapper over context.Context which allows us to build transaction unaware database logic in our applications. Instead of calling tx.Exec on a transaction we call db.Exec(ctx, ...) where context will contain transaction information that we can use.

Nested Transactions

Nested transactions are a feature used heavily in Rails or other frameworks to roll back test code to leave the database in a clean state. If you take a look at the example_test.go file you can see how we can use a setup function to rollback on error:

func setup(t *testing.T) (db.TX, func()) {
	ctx, err := db.Begin(context.Background())
	require.NoError(t, err)
	return ctx, func() { ctx.Rollback() }
}

Transaction begin takes a context.Context, if this is already a transaction, we will initialize a savepoint instead of an additional transaction. Note that additional nesting will continue to initialize savepoints. This isn't a true nesting of transactions and rollbacks will not bubble up. It's important to make sure that your code handles rolling back transactions appropriately with error handling if you need errors to bubble up.

Using db.TX is the safest way to manage transactions. The function handles rollbacks and commit calls based on the error value from the callback function.

db.TX(ctx, func(ctx context.Context) error {
	return db.TX(ctx, ...)
})

Usage With Other SQL Generators

The Query and Exec methods take an SQL interface defined below:

type SQL interface {
	SQL() (string, []interface{}, error)
}

This means that as long as a query generator implements this interface, you can pass this into any for the db package functions to execute your query. This means that you can extend query builders or bring your own query builder.

If you want to use squirrel for example, you can provide a simple little helper to translate the interfaces:

type SQ struct { squirrel.Sqlizer }

func (s SQ) SQL() (string, []interface{}, error) { return s.ToSql() }

Encoding

The encoding package handles taking golang interfaces and converting them from sql types to golang types. It uses sqlx under the hood to manage this.

One of the main differences is that it has a different philosophy from the core golang database/sql package when it comes to nullable types. Mainly, nullable types are converted into their default value in golang. Instead of requiring a pointer.

For example:

type user struct { ID string `db:"id"` }
user := &user{}

encoding.Unmarshal(user, row) // database/sql#Rows

In this case, if id in the database is NULL then it will scan simply as a blank string into user.ID.

You can customize the mapping between struct fields and database field names by specifiying a mapper function:

encoding.NewEncoder().WithMapper(reflectx.NewMapperFunc("mytag", strings.ToLower))

sqlkit's People

Contributors

colinjfw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sqlkit's Issues

Improve Documentation

Improve docs, specifically on why the package exists and provide more examples. Also add semver docs and contributing files. Also mention the example package.

Aggregate Builders

Aggregate builders that allow you to do something like:

db.Select(Count("users").As("user_count"))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.