Categorygithub.com/mc2soft/reform
modulepackage
1.3.4
Repository: https://github.com/mc2soft/reform.git
Documentation: pkg.go.dev

# README

reform

Release GoDoc Gitter Travis CI Build Status AppVeyor Build status Coverage Report Go Report Card GolangCI

Reform gopher logo

A better ORM for Go and database/sql.

It uses non-empty interfaces, code generation (go generate), and initialization-time reflection as opposed to interface{}, type system sidestepping, and runtime reflection. It will be kept simple.

Supported SQL dialects:

RDBMSLibrary and driversStatus
PostgreSQLgithub.com/lib/pq (postgres)Stable. Tested with all supported versions.
github.com/jackc/pgx/stdlib (pgx)Stable. Tested with all supported versions.
MySQLgithub.com/go-sql-driver/mysql (mysql)Stable. Tested with all supported versions.
SQLite3github.com/mattn/go-sqlite3 (sqlite3)Stable.
Microsoft SQL Servergithub.com/denisenkom/go-mssqldb (sqlserver, mssql)Stable. Tested on Windows with: SQL2008R2SP2, SQL2012SP1, SQL2014, SQL2016. On Linux with: microsoft/mssql-server-linux:latest Docker image.

Notes:

Quickstart

  1. Make sure you are using Go 1.10+. Install or update reform package, reform and reform-db commands (see about versioning below):

    go get -u github.com/mc2soft/reform/...
    
  2. Use reform-db command to generate models for your existing database schema. For example:

    reform-db -db-driver=sqlite3 -db-source=example.sqlite3 init
    
  3. Update generated models or write your own – struct representing a table or view row. For example, store this in file person.go:

    //go:generate reform
    
    //reform:people
    type Person struct {
    	ID        int32      `reform:"id,pk"`
    	Name      string     `reform:"name"`
    	Email     *string    `reform:"email"`
    	CreatedAt time.Time  `reform:"created_at"`
    	UpdatedAt *time.Time `reform:"updated_at"`
    }
    

    Magic comment //reform:people links this model to people table or view in SQL database. The first value in field's reform tag is a column name. pk marks primary key. Use value - or omit tag completely to skip a field. Use pointers (recommended) or sql.NullXXX types for nullable fields.

  4. Run reform [package or directory] or go generate [package or file]. This will create person_reform.go in the same package with type PersonTable and methods on Person.

  5. See documentation how to use it. Simple example:

    // Use reform.NewDB to create DB.
    
    // Save record (performs INSERT or UPDATE).
    person := &Person{
    	Name:  "Alexey Palazhchenko",
    	Email: pointer.ToString("[email protected]"),
    }
    if err := DB.Save(person); err != nil {
    	log.Fatal(err)
    }
    
    // ID is filled by Save.
    person2, err := DB.FindByPrimaryKeyFrom(PersonTable, person.ID)
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Println(person2.(*Person).Name)
    
    // Delete record.
    if err = DB.Delete(person); err != nil {
    	log.Fatal(err)
    }
    
    // Find records by IDs.
    persons, err := DB.FindAllFrom(PersonTable, "id", 1, 2)
    if err != nil {
    	log.Fatal(err)
    }
    for _, p := range persons {
    	fmt.Println(p)
    }
    

Background

reform was born during summer 2014 out of frustrations with existing Go ORMs. All of them have a method Save(record interface{}) which can be used like this:

orm.Save(User{Name: "gopher"})
orm.Save(&User{Name: "gopher"})
orm.Save(nil)
orm.Save("Batman!!")

Now you can say that last invocation is obviously invalid, and that it's not hard to make an ORM to accept both first and second versions. But there are two problems:

  1. Compiler can't check it. Method's signature in godoc will not tell us how to use it. We are essentially working against those tools by sidestepping type system.
  2. First version is still invalid, since one would expect Save() method to set record's primary key after INSERT, but this change will be lost due to passing by value.

First proprietary version of reform was used in production even before go generate announcement. This free and open-source version is the fourth milestone on the road to better and idiomatic API.

Versioning policy

We are following Semantic Versioning, using gopkg.in and filling a changelog.

We use branch v1-stable (default on Github) for v1 development and tags v1.Y.Z for releases. All v1 releases are SemVer-compatible, breaking changes will not be applied. Canonical import path is github.com/mc2soft/reform. go get -u github.com/mc2soft/reform/... will install the latest released version. To install not yet released v1 version one can do checkout manually while preserving import path:

cd $GOPATH/src/github.com/mc2soft/reform
git fetch
git checkout origin/v1-stable
go install -v github.com/mc2soft/reform/reform

Additional packages

Caveats and limitations

  • There should be zero pk fields for Struct and exactly one pk field for Record. Composite primary keys are not supported (#114).
  • pk field can't be a pointer (== nil doesn't work).
  • Database row can't have a Go's zero value (0, empty string, etc.) in primary key column.

License

Code is covered by standard MIT-style license. Copyright (c) 2016-2018 Alexey Palazhchenko. See LICENSE for details. Note that generated code is covered by the terms of your choice.

The reform gopher was drawn by Natalya Glebova. Please use it only as reform logo. It is based on the original design by Renée French, released under Creative Commons Attribution 3.0 USA license.

Contributing

See Contributing Guidelines.

# Packages

Package dialects implements reform.Dialect selector.
Package parse implements parsing of Go structs in files and runtime.
Package reform implements reform command.
Package reform-db implements reform-db command.

# Functions

Inspect returns suitable for logging representation of a query argument.
NewDB creates new DB object for given SQL database connection.
NewDBFromInterface creates new DB object for given DBInterface.
NewPrintfLogger creates a new simple query logger for any Printf-like function.
NewTX creates new TX object for given SQL database transaction.
NewTXFromInterface creates new TX object for given TXInterface.

# Constants

DefaultValues is a method using "DEFAULT VALUES".
EmptyLists is a method using "() VALUES ()".
LastInsertId is method using sql.Result.LastInsertId().
Limit is a method using "LIMIT N" SQL syntax.
OutputInserted is method using "OUTPUT INSERTED.id" SQL syntax.
Returning is method using "RETURNING id" SQL syntax.
SelectTop is a method using "SELECT TOP N" SQL syntax.
Version defines reform version.

# Variables

ErrNoPK is returned from various methods when primary key is required and not set.
ErrNoRows is returned from various methods when query produced no rows.
ErrTxDone is returned from Commit() and Rollback() TX methods when transaction is already committed or rolled back.

# Structs

DB represents a connection to SQL database.
PrintfLogger is a simple query logger.
Querier performs queries and commands.
TX represents a SQL database transaction.

# Interfaces

AfterFinder is an optional interface for Record which is used by Querier's finders and selectors.
BeforeInserter is an optional interface for Record which is used by Querier.Insert.
BeforeUpdater is an optional interface for Record which is used by Querier.Update and Querier.UpdateColumns.
DBInterface is a subset of *sql.DB used by reform.
DBTX is an interface for database connection or transaction.
Dialect represents differences in various SQL dialects.
Logger is responsible to log queries before and after their execution.
Record represents a row in SQL database table with single-column primary key.
Struct represents a row in SQL database view or table.
Table represents SQL database table with single-column primary key.
TXInterface is a subset of *sql.Tx used by reform.
View represents SQL database view or table.

# Type aliases

DefaultValuesMethod is a method of inserting of row with all default values.
LastInsertIdMethod is a method of receiving primary key of last inserted row.
Printf is a (fmt.Printf|log.Printf|testing.T.Logf)-like function.
SelectLimitMethod is a method of limiting the number of rows in a query result.