Giter Club home page Giter Club logo

gorp's Introduction

Go Relational Persistence

build status issues Go Reference

Update 2016-11-13: Future versions

As many of the maintainers have become busy with other projects, progress toward the ever-elusive v2 has slowed to the point that we're only occasionally making progress outside of merging pull requests. In the interest of continuing to release, I'd like to lean toward a more maintainable path forward.

For the moment, I am releasing a v2 tag with the current feature set from master, as some of those features have been actively used and relied on by more than one project. Our next goal is to continue cleaning up the code base with non-breaking changes as much as possible, but if/when a breaking change is needed, we'll just release new versions. This allows us to continue development at whatever pace we're capable of, without delaying the release of features or refusing PRs.

Introduction

I hesitate to call gorp an ORM. Go doesn't really have objects, at least not in the classic Smalltalk/Java sense. There goes the "O". gorp doesn't know anything about the relationships between your structs (at least not yet). So the "R" is questionable too (but I use it in the name because, well, it seemed more clever).

The "M" is alive and well. Given some Go structs and a database, gorp should remove a fair amount of boilerplate busy-work from your code.

I hope that gorp saves you time, minimizes the drudgery of getting data in and out of your database, and helps your code focus on algorithms, not infrastructure.

  • Bind struct fields to table columns via API or tag
  • Support for embedded structs
  • Support for transactions
  • Forward engineer db schema from structs (great for unit tests)
  • Pre/post insert/update/delete hooks
  • Automatically generate insert/update/delete statements for a struct
  • Automatic binding of auto increment PKs back to struct after insert
  • Delete by primary key(s)
  • Select by primary key(s)
  • Optional trace sql logging
  • Bind arbitrary SQL queries to a struct
  • Bind slice to SELECT query results without type assertions
  • Use positional or named bind parameters in custom SELECT queries
  • Optional optimistic locking using a version column (for update/deletes)

Installation

Use go get or your favorite vendoring tool, using whichever import path you'd like.

Versioning

We use semantic version tags. Feel free to import through gopkg.in (e.g. gopkg.in/gorp.v2) to get the latest tag for a major version, or check out the tag using your favorite vendoring tool.

Development is not very active right now, but we have plans to restructure gorp as we continue to move toward a more extensible system. Whenever a breaking change is needed, the major version will be bumped.

The master branch is where all development is done, and breaking changes may happen from time to time. That said, if you want to live on the bleeding edge and are comfortable updating your code when we make a breaking change, you may use github.com/go-gorp/gorp as your import path.

Check the version tags to see what's available. We'll make a good faith effort to add badges for new versions, but we make no guarantees.

Supported Go versions

This package is guaranteed to be compatible with the latest 2 major versions of Go.

Any earlier versions are only supported on a best effort basis and can be dropped any time. Go has a great compatibility promise. Upgrading your program to a newer version of Go should never really be a problem.

Migration guide

Pre-v2 to v2

Automatic mapping of the version column used in optimistic locking has been removed as it could cause problems if the type was not int. The version column must now explicitly be set with tablemap.SetVersionCol().

Help/Support

Use our gitter channel. We used to use IRC, but with most of us being pulled in many directions, we often need the email notifications from gitter to yell at us to sign in.

Quickstart

package main

import (
    "database/sql"
    "gopkg.in/gorp.v1"
    _ "github.com/mattn/go-sqlite3"
    "log"
    "time"
)

func main() {
    // initialize the DbMap
    dbmap := initDb()
    defer dbmap.Db.Close()

    // delete any existing rows
    err := dbmap.TruncateTables()
    checkErr(err, "TruncateTables failed")

    // create two posts
    p1 := newPost("Go 1.1 released!", "Lorem ipsum lorem ipsum")
    p2 := newPost("Go 1.2 released!", "Lorem ipsum lorem ipsum")

    // insert rows - auto increment PKs will be set properly after the insert
    err = dbmap.Insert(&p1, &p2)
    checkErr(err, "Insert failed")

    // use convenience SelectInt
    count, err := dbmap.SelectInt("select count(*) from posts")
    checkErr(err, "select count(*) failed")
    log.Println("Rows after inserting:", count)

    // update a row
    p2.Title = "Go 1.2 is better than ever"
    count, err = dbmap.Update(&p2)
    checkErr(err, "Update failed")
    log.Println("Rows updated:", count)

    // fetch one row - note use of "post_id" instead of "Id" since column is aliased
    //
    // Postgres users should use $1 instead of ? placeholders
    // See 'Known Issues' below
    //
    err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id)
    checkErr(err, "SelectOne failed")
    log.Println("p2 row:", p2)

    // fetch all rows
    var posts []Post
    _, err = dbmap.Select(&posts, "select * from posts order by post_id")
    checkErr(err, "Select failed")
    log.Println("All rows:")
    for x, p := range posts {
        log.Printf("    %d: %v\n", x, p)
    }

    // delete row by PK
    count, err = dbmap.Delete(&p1)
    checkErr(err, "Delete failed")
    log.Println("Rows deleted:", count)

    // delete row manually via Exec
    _, err = dbmap.Exec("delete from posts where post_id=?", p2.Id)
    checkErr(err, "Exec failed")

    // confirm count is zero
    count, err = dbmap.SelectInt("select count(*) from posts")
    checkErr(err, "select count(*) failed")
    log.Println("Row count - should be zero:", count)

    log.Println("Done!")
}

type Post struct {
    // db tag lets you specify the column name if it differs from the struct field
    Id      int64  `db:"post_id"`
    Created int64
    Title   string `db:",size:50"`               // Column size set to 50
    Body    string `db:"article_body,size:1024"` // Set both column name and size
}

func newPost(title, body string) Post {
    return Post{
        Created: time.Now().UnixNano(),
        Title:   title,
        Body:    body,
    }
}

func initDb() *gorp.DbMap {
    // connect to db using standard Go database/sql API
    // use whatever database/sql driver you wish
    db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
    checkErr(err, "sql.Open failed")

    // construct a gorp DbMap
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}

    // add a table, setting the table name to 'posts' and
    // specifying that the Id property is an auto incrementing PK
    dbmap.AddTableWithName(Post{}, "posts").SetKeys(true, "Id")

    // create the table. in a production system you'd generally
    // use a migration tool, or create the tables via scripts
    err = dbmap.CreateTablesIfNotExists()
    checkErr(err, "Create tables failed")

    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err)
    }
}

Examples

Mapping structs to tables

First define some types:

type Invoice struct {
    Id       int64
    Created  int64
    Updated  int64
    Memo     string
    PersonId int64
}

type Person struct {
    Id      int64
    Created int64
    Updated int64
    FName   string
    LName   string
}

// Example of using tags to alias fields to column names
// The 'db' value is the column name
//
// A hyphen will cause gorp to skip this field, similar to the
// Go json package.
//
// This is equivalent to using the ColMap methods:
//
//   table := dbmap.AddTableWithName(Product{}, "product")
//   table.ColMap("Id").Rename("product_id")
//   table.ColMap("Price").Rename("unit_price")
//   table.ColMap("IgnoreMe").SetTransient(true)
//
// You can optionally declare the field to be a primary key and/or autoincrement
//
type Product struct {
    Id         int64     `db:"product_id, primarykey, autoincrement"`
    Price      int64     `db:"unit_price"`
    IgnoreMe   string    `db:"-"`
}

Then create a mapper, typically you'd do this one time at app startup:

// connect to db using standard Go database/sql API
// use whatever database/sql driver you wish
db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword")

// construct a gorp DbMap
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}

// register the structs you wish to use with gorp
// you can also use the shorter dbmap.AddTable() if you
// don't want to override the table name
//
// SetKeys(true) means we have a auto increment primary key, which
// will get automatically bound to your struct post-insert
//
t1 := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
t2 := dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id")
t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")

Struct Embedding

gorp supports embedding structs. For example:

type Names struct {
    FirstName string
    LastName  string
}

type WithEmbeddedStruct struct {
    Id int64
    Names
}

es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
err := dbmap.Insert(es)

See the TestWithEmbeddedStruct function in gorp_test.go for a full example.

Create/Drop Tables

Automatically create / drop registered tables. This is useful for unit tests but is entirely optional. You can of course use gorp with tables created manually, or with a separate migration tool (like sql-migrate, goose or migrate).

// create all registered tables
dbmap.CreateTables()

// same as above, but uses "if not exists" clause to skip tables that are
// already defined
dbmap.CreateTablesIfNotExists()

// drop
dbmap.DropTables()

SQL Logging

Optionally you can pass in a logger to trace all SQL statements. I recommend enabling this initially while you're getting the feel for what gorp is doing on your behalf.

Gorp defines a GorpLogger interface that Go's built in log.Logger satisfies. However, you can write your own GorpLogger implementation, or use a package such as glog if you want more control over how statements are logged.

// Will log all SQL statements + args as they are run
// The first arg is a string prefix to prepend to all log messages
dbmap.TraceOn("[gorp]", log.New(os.Stdout, "myapp:", log.Lmicroseconds))

// Turn off tracing
dbmap.TraceOff()

Insert

// Must declare as pointers so optional callback hooks
// can operate on your data, not copies
inv1 := &Invoice{0, 100, 200, "first order", 0}
inv2 := &Invoice{0, 100, 200, "second order", 0}

// Insert your rows
err := dbmap.Insert(inv1, inv2)

// Because we called SetKeys(true) on Invoice, the Id field
// will be populated after the Insert() automatically
fmt.Printf("inv1.Id=%d  inv2.Id=%d\n", inv1.Id, inv2.Id)

Update

Continuing the above example, use the Update method to modify an Invoice:

// count is the # of rows updated, which should be 1 in this example
count, err := dbmap.Update(inv1)

Delete

If you have primary key(s) defined for a struct, you can use the Delete method to remove rows:

count, err := dbmap.Delete(inv1)

Select by Key

Use the Get method to fetch a single row by primary key. It returns nil if no row is found.

// fetch Invoice with Id=99
obj, err := dbmap.Get(Invoice{}, 99)
inv := obj.(*Invoice)

Ad Hoc SQL

SELECT

Select() and SelectOne() provide a simple way to bind arbitrary queries to a slice or a single struct.

// Select a slice - first return value is not needed when a slice pointer is passed to Select()
var posts []Post
_, err := dbmap.Select(&posts, "select * from post order by id")

// You can also use primitive types
var ids []string
_, err := dbmap.Select(&ids, "select id from post")

// Select a single row.
// Returns an error if no row found, or if more than one row is found
var post Post
err := dbmap.SelectOne(&post, "select * from post where id=?", id)

Want to do joins? Just write the SQL and the struct. gorp will bind them:

// Define a type for your join
// It *must* contain all the columns in your SELECT statement
//
// The names here should match the aliased column names you specify
// in your SQL - no additional binding work required.  simple.
//
type InvoicePersonView struct {
    InvoiceId   int64
    PersonId    int64
    Memo        string
    FName       string
}

// Create some rows
p1 := &Person{0, 0, 0, "bob", "smith"}
err = dbmap.Insert(p1)
checkErr(err, "Insert failed")

// notice how we can wire up p1.Id to the invoice easily
inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id}
err = dbmap.Insert(inv1)
checkErr(err, "Insert failed")

// Run your query
query := "select i.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " +
	"from invoice_test i, person_test p " +
	"where i.PersonId = p.Id"

// pass a slice to Select()
var list []InvoicePersonView
_, err := dbmap.Select(&list, query)

// this should test true
expected := InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName}
if reflect.DeepEqual(list[0], expected) {
    fmt.Println("Woot! My join worked!")
}

SELECT string or int64

gorp provides a few convenience methods for selecting a single string or int64.

// select single int64 from db (use $1 instead of ? for postgresql)
i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)

// select single string from db:
s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)

Named bind parameters

You may use a map or struct to bind parameters by name. This is currently only supported in SELECT queries.

_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{
  "name": "Rob",
  "age": 31,
})

UPDATE / DELETE

You can execute raw SQL if you wish. Particularly good for batch operations.

res, err := dbmap.Exec("delete from invoice_test where PersonId=?", 10)

Transactions

You can batch operations into a transaction:

func InsertInv(dbmap *DbMap, inv *Invoice, per *Person) error {
    // Start a new transaction
    trans, err := dbmap.Begin()
    if err != nil {
        return err
    }

    err = trans.Insert(per)
    checkErr(err, "Insert failed")

    inv.PersonId = per.Id
    err = trans.Insert(inv)
    checkErr(err, "Insert failed")

    // if the commit is successful, a nil error is returned
    return trans.Commit()
}

Hooks

Use hooks to update data before/after saving to the db. Good for timestamps:

// implement the PreInsert and PreUpdate hooks
func (i *Invoice) PreInsert(s gorp.SqlExecutor) error {
    i.Created = time.Now().UnixNano()
    i.Updated = i.Created
    return nil
}

func (i *Invoice) PreUpdate(s gorp.SqlExecutor) error {
    i.Updated = time.Now().UnixNano()
    return nil
}

// You can use the SqlExecutor to cascade additional SQL
// Take care to avoid cycles. gorp won't prevent them.
//
// Here's an example of a cascading delete
//
func (p *Person) PreDelete(s gorp.SqlExecutor) error {
    query := "delete from invoice_test where PersonId=?"
    
    _, err := s.Exec(query, p.Id)
    
    if err != nil {
        return err
    }
    return nil
}

Full list of hooks that you can implement:

PostGet
PreInsert
PostInsert
PreUpdate
PostUpdate
PreDelete
PostDelete

All have the same signature.  for example:

func (p *MyStruct) PostUpdate(s gorp.SqlExecutor) error

Optimistic Locking

Note that this behaviour has changed in v2. See Migration Guide.

gorp provides a simple optimistic locking feature, similar to Java's JPA, that will raise an error if you try to update/delete a row whose version column has a value different than the one in memory. This provides a safe way to do "select then update" style operations without explicit read and write locks.

// Version is an auto-incremented number, managed by gorp
// If this property is present on your struct, update
// operations will be constrained
//
// For example, say we defined Person as:

type Person struct {
    Id       int64
    Created  int64
    Updated  int64
    FName    string
    LName    string

    // automatically used as the Version col
    // use table.SetVersionCol("columnName") to map a different
    // struct field as the version field
    Version  int64
}

p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
err = dbmap.Insert(p1)  // Version is now 1
checkErr(err, "Insert failed")

obj, err := dbmap.Get(Person{}, p1.Id)
p2 := obj.(*Person)
p2.LName = "Edwards"
_,err = dbmap.Update(p2)  // Version is now 2
checkErr(err, "Update failed")

p1.LName = "Howard"

// Raises error because p1.Version == 1, which is out of date
count, err := dbmap.Update(p1)
_, ok := err.(gorp.OptimisticLockError)
if ok {
    // should reach this statement

    // in a real app you might reload the row and retry, or
    // you might propegate this to the user, depending on the desired
    // semantics
    fmt.Printf("Tried to update row with stale data: %v\n", err)
} else {
    // some other db error occurred - log or return up the stack
    fmt.Printf("Unknown db err: %v\n", err)
}

Adding INDEX(es) on column(s) beyond the primary key

Indexes are frequently critical for performance. Here is how to add them to your tables.

NB: SqlServer and Oracle need testing and possible adjustment to the CreateIndexSuffix() and DropIndexSuffix() methods to make AddIndex() work for them.

In the example below we put an index both on the Id field, and on the AcctId field.

type Account struct {
	Id      int64
	AcctId  string // e.g. this might be a long uuid for portability
}

// indexType (the 2nd param to AddIndex call) is "Btree" or "Hash" for MySQL.
// demonstrate adding a second index on AcctId, and constrain that field to have unique values.
dbm.AddTable(iptab.Account{}).SetKeys(true, "Id").AddIndex("AcctIdIndex", "Btree", []string{"AcctId"}).SetUnique(true)

err = dbm.CreateTablesIfNotExists()
checkErr(err, "CreateTablesIfNotExists failed")

err = dbm.CreateIndex()
checkErr(err, "CreateIndex failed")

Check the effect of the CreateIndex() call in mysql:

$ mysql

MariaDB [test]> show create table Account;
+---------+--------------------------+
| Account | CREATE TABLE `Account` (
  `Id` bigint(20) NOT NULL AUTO_INCREMENT,
  `AcctId` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE   <<<--- yes! index added.
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
+---------+--------------------------+

Database Drivers

gorp uses the Go 1 database/sql package. A full list of compliant drivers is available here:

http://code.google.com/p/go-wiki/wiki/SQLDrivers

Sadly, SQL databases differ on various issues. gorp provides a Dialect interface that should be implemented per database vendor. Dialects are provided for:

  • MySQL
  • PostgreSQL
  • sqlite3

Each of these three databases pass the test suite. See gorp_test.go for example DSNs for these three databases.

Support is also provided for:

  • Oracle (contributed by @klaidliadon)
  • SQL Server (contributed by @qrawl) - use driver: github.com/denisenkom/go-mssqldb

Note that these databases are not covered by CI and I (@coopernurse) have no good way to test them locally. So please try them and send patches as needed, but expect a bit more unpredicability.

Sqlite3 Extensions

In order to use sqlite3 extensions you need to first register a custom driver:

import (
	"database/sql"

	// use whatever database/sql driver you wish
	sqlite "github.com/mattn/go-sqlite3"
)

func customDriver() (*sql.DB, error) {

	// create custom driver with extensions defined
	sql.Register("sqlite3-custom", &sqlite.SQLiteDriver{
		Extensions: []string{
			"mod_spatialite",
		},
	})

	// now you can then connect using the 'sqlite3-custom' driver instead of 'sqlite3'
	return sql.Open("sqlite3-custom", "/tmp/post_db.bin")
}

Known Issues

SQL placeholder portability

Different databases use different strings to indicate variable placeholders in prepared SQL statements. Unlike some database abstraction layers (such as JDBC), Go's database/sql does not standardize this.

SQL generated by gorp in the Insert, Update, Delete, and Get methods delegates to a Dialect implementation for each database, and will generate portable SQL.

Raw SQL strings passed to Exec, Select, SelectOne, SelectInt, etc will not be parsed. Consequently you may have portability issues if you write a query like this:

// works on MySQL and Sqlite3, but not with Postgresql err :=
dbmap.SelectOne(&val, "select * from foo where id = ?", 30)

In Select and SelectOne you can use named parameters to work around this. The following is portable:

err := dbmap.SelectOne(&val, "select * from foo where id = :id",
map[string]interface{} { "id": 30})

Additionally, when using Postgres as your database, you should utilize $1 instead of ? placeholders as utilizing ? placeholders when querying Postgres will result in pq: operator does not exist errors. Alternatively, use dbMap.Dialect.BindVar(varIdx) to get the proper variable binding for your dialect.

time.Time and time zones

gorp will pass time.Time fields through to the database/sql driver, but note that the behavior of this type varies across database drivers.

MySQL users should be especially cautious. See: ziutek/mymysql#77

To avoid any potential issues with timezone/DST, consider:

Running the tests

The included tests may be run against MySQL, Postgresql, or sqlite3. You must set two environment variables so the test code knows which driver to use, and how to connect to your database.

# MySQL example:
export GORP_TEST_DSN=gomysql_test/gomysql_test/abc123
export GORP_TEST_DIALECT=mysql

# run the tests
go test

# run the tests and benchmarks
go test -bench="Bench" -benchtime 10

Valid GORP_TEST_DIALECT values are: "mysql"(for mymysql), "gomysql"(for go-sql-driver), "postgres", "sqlite" See the test_all.sh script for examples of all 3 databases. This is the script I run locally to test the library.

Performance

gorp uses reflection to construct SQL queries and bind parameters. See the BenchmarkNativeCrud vs BenchmarkGorpCrud in gorp_test.go for a simple perf test. On my MacBook Pro gorp is about 2-3% slower than hand written SQL.

Contributors

  • matthias-margush - column aliasing via tags
  • Rob Figueiredo - @robfig
  • Quinn Slack - @sqs

gorp's People

Contributors

benprew avatar billmccord avatar bkali avatar coopernurse avatar dmajere avatar geertjohan avatar glycerine avatar joho avatar klaidliadon avatar kungfumike avatar lbyte00 avatar matthias-margush avatar mlb5000 avatar nelsam avatar nexneo avatar noahshen avatar ottob avatar purohit avatar pwaller avatar rafaeljusto avatar robfig avatar rubenv avatar seehuhn avatar songmu avatar sqdk avatar sqs avatar tmc avatar tobischo avatar umisama avatar vinceyuan 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  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

gorp's Issues

Reflection error using Get Method

Im getting a panic message from the reflection lib

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

ConfigCMap  :=      dbmap.AddTableWithName(ConfigClient{},"config_clients").SetKeys(true,"client_id")

obj, err := dbmap.Get(ConfigClient{}, 1000)

it panic a t the Get call.

the table and struct had the same names(except for 1 field named type)
looks some kind of mismatch but i dont know how to debug any further

Embedded structs don't appear to work on Postgres

Hi

I am unable to get embedded structs to work with Postgres. I tried the "WithEmbeddedStruct" example straight from the Readme and the gorp_test.go. Unfortunately, I get the following errors

INFO  2013/10/19 17:18:46 gorp.go:908: [gorp] insert into "embeddedstruct" ("id","names") values (default,$1) returning id; [[{Alice Smith}]]
INFO  2013/10/19 17:18:46 myService.go:61: Error in Create of AppointmentService sql: converting Exec argument #0's type: unsupported type models.Names, a struct

Versions
Postgres: 9.3
Go : 1.1
OS : Mountain Lion

Am I doing something wrong? Your time is appreciated

Thanks
-Rv

Add new function CreateTablesIfNotExists

CreateTables is very useful for testing. For completeness, it would be useful to have either

  • a new CreateTablesIfNotExists function that does what you'd expect from its name, or
  • a new boolean parameter on CreateTables that enables/disables an "if not exists" clause.

SelectDate

How about a SelectDate to go along with SelectInt and friends?

(Arguably should be called SelectTime since that's the Go type name.)

Improve interface for list of results

Presently, when I get a list of results back, I have to loop through them to convert the individual elements to the right type:

    results, _ := dbm.Select(Fortune{}, "SELECT * FROM Fortune")

    var fortunes []*Fortune
    for _, r := range results {
        fortunes = append(fortunes, r.(*Fortune))
    }

I think this work could be avoided if instead the caller could pass the slice to populate, e.g.

    var fortunes []*Fortune
    _, err := dbm.Select(&fortunes, "SELECT * FROM Fortune")

Any thoughts?

(Thanks for the great library!)

gorp should throw error if auto increment is true and primary key isn't a numeric column

I spent quite a while banging my head against the wall trying to figure out what was wrong with my program because .Insert() threw this error:

panic: reflect: call of reflect.Value.SetInt on string Value

which it turns out traced back to gorp.go:1363:

elem.Field(bi.autoIncrIdx).SetInt(id)

Turns out I had done .SetKeys(true, "a_string_column"). This was especially hard for me to track down due to logical complexities with my program.

gorp should throw a more helpful error if you try to set autoincrement on a primary key column that's not numeric.

Need some way of ignoring NULL-able foreign keys for INSERT/UPDATE

Hello and thank you for providing Gorp! :)

Now, I have a model with several foreign keys that are allowed to be set to NULL.

type MyModel struct {
    Id             int    `db:"id"`
    Name           string `db:"name"`
    ProjectId      int    `db:"project_id"`    // Allows NULL
    CustomerId     int    `db:"customer_id"`    // Allows NULL
}

AddTableWithName(MyModel{}, "mymodels").SetKeys(true, "Id").SetKeys(false, "ProjectId", "CustomerId")

(Tables are created with SQL-scripts; not with CreateTables). I insert new rows like so

db.Insert(&MyModel{Name:"Hello"})

This will return an error:

test:19:17:47.078572 [gorp 3] insert into "mymodels" ("id","name","project_id","customer_id") values (default,$1,$2,$3) returning id; [[Hello 0 0]]

As there is not project or customer with id=0, Postgres will yell loudly. In this case I want to make Gorp drop project_id and customer_id from the sql. Problem is, how do I do that?

Fix error message when non-slice pointer passed

From @sqs comment in #40

If you try to select into var foo []_MyType instead of a var foo *[]_MyType (e.g., you forget the & in the arg), then you get an error message that is no longer applicable with this new feature:
gorp: Cannot SELECT into non-struct type: []*MyType

time.Time support

I'm not sure if this is something you want to support, but Sqlite (and presumably other databases) does not support a formal datetime type. It should be possible to .Format() and .Parse() the time.Time field from a String type. I'm not sure how this would be configured, though, or if it's too niche of a feature.

FWIW, I tried to implement time.Time support in the go-sqlite3 driver, but I couldn't find a way to do so within the constraints of database/sql

Verbose COUNT(*) Code

Is there any way to make this code more concise? I don't know if I'm using gorp incorrectly, and it seems like a lot of code for a simple task:

func (u *User) NumRecords(s gorp.SqlExecutor) int {
    type Count struct {
        Count int
    }
    rows, err := s.Select(Count{}, "SELECT COUNT(*) as Count FROM Queue WHERE User_Id = $1", u.Id)
    // (handle err)
    count := rows[0].(*Count)
    return count.Count
}           

Also, I'm confused as to the purpose of passing the Count{} as an argument to Select, if I have to type assert it later anyway.

I couldn't use .SelectInt like I wanted since it's a SqlExecutor and not a Transaction. Any advice appreciated.

unknown value type for bind (insert)

First thank you very much for nice ORM, now it's one of the most documented DB libraries for Go and one of a few up-to-date.

Go's native type "bool" is not supported. Consider this:

type PersistentUser struct {
Key int32
Id string
PassedTraining bool
}

this code will panic (panic: unknown value type for bind)

pu := &PersistentUser{43,"33r",false}

// Insert your rows
err := dbmap.Insert(pu)
if err!=nil {
panic(err)
}

Windows 7/x86, MySql 5.1, Go weekly.2011-12-22 11071

Structure embedding and modelling

type Base struct {
    Created int64   `db:"created"`
    Updated int64   `db:"updated"`
}

func (b *Base) PreInsert(s gorp.SqlExecutor) error {
    b.Created = time.Now().UnixNano()
    b.Updated = b.Created
    return nil
}

func (b *Base) PreUpdate(s gorp.SqlExecutor) error {
    b.Updated = time.Now().UnixNano()
    return nil
}

type Post struct {
    Id int  `db:"id"`
    Name string `db:"name"`
    Title string    `db:"title"`
    Content string  `db:"content"`
    *Base
}


dbmap.createTables creates Post table with a member Base varchar(255). Does gorp support embedding structs? If yes, how do I do it?

Add dbmap.DropTablesIfExists()

Now that we have CreateTablesIfNotExists(), it seems appropriate to add the related DropTablesIfExists() function.

A cursory glance of the mysql/postgresql/sqlite docs suggests the syntax is supported and identical in all three databases.

Received #1064 error from MySQL server: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for ...

dbmap.CreateTables() returns sql error. Driver is mymysql

My code:

package robo

import(
"fmt"
"meteor"
"exp/sql"
"github.com/coopernurse/gorp"
_ "github.com/ziutek/mymysql/godrv"
)

//var db mysql.Conn
var db *sql.DB
var dbmap *gorp.DbMap

func Startup(){

db, err := sql.Open("mymysql", "dbname/user/pwd")
meteor.CheckError(err)

// construct a gorp DbMap
dbmap = &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}

dbmap.AddTable(PersistentUser{}).SetKeys(true, "Key")


//meteor.CheckError(dbmap.DropTables())
meteor.CheckError(dbmap.CreateTables())   // <-----------------  error here

}

func TestDao(){
testInsert()
}

func testInsert(){

Startup()

pu := &PersistentUser{-1,"33r",false}

for i:=0;i<10;i++{
    pu.Id = fmt.Sprintf("user%d",i)
    pu.PassedTraining = false;      
    err := dbmap.Insert(pu)     
    meteor.CheckError(err)
}

Shutdown()

}

func Shutdown(){
if db!=nil {
db.Close()
}

db = nil
dbmap = nil

}

and

type PersistentUser struct {
Key int32
Id string
PassedTraining bool
}

after line meteor.CheckError(dbmap.CreateTables()) we get this:
panic: Fatal error: Received #1064 error from MySQL server: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'int not null auto_increment, Id varchar(255), PassedTraining varchar(255), prima' at line 1"

Slice Support

When trying to Insert a struct containing a slice, I get this error:

sql: converting argument #1's type: unsupported type []float64, a slice

The struct field is []float64 and the type of the column in Postgres is real[].

Does gorp support slices and this error is particular to my program or is this yet unimplemented?

Foreign key?

Hello! It's very interesting project - now I'm studying his abilities. And I cannot understand, how can I implement foreign key at the model level?

For example:

type First struct {
    Id int
    Name string
    SecondId int
}
type Second struct {
    Id int
    Name string
}

If I understand correctly, selection of the First object with the Second may looks like this:

first, err := dbmap.Get(First{}, first_id)
first = first.(*First)
second, err := dbmap.Get(Second{}, first.SecondId)
second = second.(*Second)

Is there any way to do it like in Django Orm (model.ForeignKey(Second)):

first, err := dbmap.Get(First{}, first_id)
first = first.(*First)
second := first.Second

Probably error in documentation

In tests you use mysql driver "github.com/ziutek/mymysql/godrv". With this driver
db, err := sql.Open("mysql", "myuser:mypassword@localhost:3306/dbname") // at https://github.com/coopernurse/gorp
doesn't work.

Either documentation should be fixed to
db, err := sql.Open("mymysql", "gomysql_test/gomysql_test/abc123")
or please point at mysql driver to use for current version of documentation

And sorry, don't know how to ask other way. In docs nothing is said about multithreading. I have many goroutines. Can I share
two global variables
var db *sql.DB
var dbmap *gorp.DbMap
among goroutines ? Or should I create single dbmap per goroutine (but single db *sql.DB)? Couple of lines in documentation would be very useful

A "fetchAll" function that takes a "Query" type."

I would like to propose adding a function that takes a "Query" type and returns a set of records, similar to the way the framework/model package in sawsij does:

http://godoc.org/bitbucket.org/jaybill/sawsij/framework/model#Table.FetchAll
http://godoc.org/bitbucket.org/jaybill/sawsij/framework/model#Query

The function would take a Query, generate a select statement based on it and return the results. This would of course have to take into account the dialect of SQL being used.

This probably shouldn't be called "fetchAll" and it shouldn't replace the existing functionality, just augment it.

Bad SQL syntax emitted for UPDATE queries on tables whose cols are all primary keys

If you have a struct like

type Foo struct {
 A, B string
}

and you call .AddTable(Foo{}).SetKeys(false, "A", "B"), then the following invalid SQL gets emitted for UPDATEs (notice the dangling where):

log:18:16:04.189098 [sql] update "foo" set  where "a"=$1 and "b"=$2; [[myval1 myval2]]

This is probably a rare case, since the update is a no-op from gorp's POV, but the update query could cause SQL triggers to be issued, so it should not fail.

how can I get the sql.Result when I use txn.Insert(model)

I have a user model, I want to use Insert(list ...interface{}) error,but it returns an error type;

gorp provides an Exec(query string, args ...interface{}) (sql.Result, error) method, but its args are string , may you provides a method which args are list inerface{} and return an sql.Result ?

[Feature Request] Select into primitive slices

When selecting a list of ids, it would be ideal to select into a []int, (or names into a []string, etc). This does not appear to be possible at the moment, without creating a struct type with a single int field.

Close prepared statement in (*Transaction).Exec to avoid leaking PostgreSQL prepared statements

When I run a lot of queries in a single connection, PostgreSQL's memory usage steadily increases (up to 13 GB after ~100k queries, at which point my computer locks up). I think this is because it does not garbage-collect prepared statements that are created in a connection until the connection is closed. The following patch fixes the issue; PostgreSQL memory usage is drastically lower after running 100k queries.

Has anybody else had the same issue?

diff --git a/gorp.go b/gorp.go
index 4086b6b..b7bbfc6 100644
--- a/gorp.go
+++ b/gorp.go
@@ -931,6 +931,7 @@ func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error
        if err != nil {
                return nil, err
        }
+       defer stmt.Close()
        return stmt.Exec(args...)
 }

example for transaction

can you add an example how we can bundle multiple update or insert in a single transaction?

sql_test file still valid?

Since I'm testing in PostgreSQL I always get an error from this file. Perhaps it can be removed from the repo?

Intelligent default column mapping

I think it'd be nice to map a struct field named

FooBarId to a column foo_bar_id

Would you be open to either a) changing the default mapping scheme or b) opening this up via some flag/api/configuration?

Allow access to underlying sql.Transaction

Running a "select count(*) from Foo" does not seem to be possible using Gorp (.Exec returns a sql.Result, which does not seem to provide access to any Row data). This would not be a problem if the Gorp Transaction type provided access to the underlying sql.Transaction, but unfortunately it does not. What do you think about making that a public member, or exposing forwarded Query / QueryRow methods?

What to do with NULL values?

Question (request for doc enhancement :)

I have a database which contains NULL values (mysql). When I try to fill a struct using

 obj, err := dbmap.Get(mystruct{}, id)

I get this error:

sql: Scan error on column index 11: converting string "<nil>" to a int: strconv.ParseInt: parsing "<nil>": invalid syntax

Which makes sense, since I have NULL values in the table which cannot be converted to an int (the requested column / struct type is int).

Is there any automatic way to treat NULL values as 0 (for example) without changing the database?

[Feature Request] Named query arguments

One thing that sqlx has is the ability to bind named query arguments from a struct or map. How do you feel about extending the existing query functions to something like this:

Select(&rowObjs, query, params…)

if len(params) == 1 {
  if params is a struct ? bind query from struct fields
  if params is a map[string]interface ? bind query from map
} else if each param is a simple value {
  // existing behavior
  bind to placeholders
} else {
  return error
}

Gorp fails to compile with Go1.1

Trying to build the current tip fails thusly:

./gorp.go:699: too many arguments in call to insert
./gorp.go:699: insert(m, m, list...) used as value
./gorp.go:713: too many arguments in call to update
./gorp.go:713: not enough arguments to return
./gorp.go:746: too many arguments in call to get
./gorp.go:746: not enough arguments to return
./gorp_test.go:783: insert redeclared in this block
previous declaration at ./gorp.go:1339
./gorp_test.go:790: update redeclared in this block
previous declaration at ./gorp.go:1290
./gorp_test.go:807: get redeclared in this block
previous declaration at ./gorp.go:1188
./gorp_test.go:860: rawselect redeclared in this block
previous declaration at ./gorp.go:1032
./gorp.go:746: too many errors
FAIL gorp [build failed]

Update selectStr, selectInt examples in README (or, a bug with parameterizing queries)

README currently shows examples:

// select single int64 from db:
i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)

// select single string from db:
s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)

These work with MySQL but not withj lib/pq (the ? doesn't seem to get replaced with the parameter value).

I'm not actually sure whether this is an old/incorrect way of parameterizing or a bug, but I was only able to to get this to work with this kind of syntax:

s, err := dbmap.SelectStr("select name from foo where blah=$1", blahVal)

Embedded Time structs fail in PostgreSql

I have a data structure:

type Times struct {
    One time.Time
    Two time.Time
}

type EmbeddedTime struct {
    Id string
    Times
}

Using this structure I'm able to create a table and insert structs into it, but when calling get, I get an error:

[CreateDb] select "id","one","two" from "embeddedstruct" where "id"=$1; [[testId1]]
Error getting from db! sql: Scan error on column index 5: unsupported driver -> Scan pair: []uint8 -> *time.Time

This seems like an error in the SQL module or in the way that GORP represents these time fields to the Scanner..

Postgres support completely broken

We've encountered a crippling problem in gorp's Postgres support while using it at Braintree.

The problem

Currently, when a row with an autoincrementing primary key is inserted, grop returns the newly generated ID to the struct by running SELECT currval on the table that it just INSERTED into. This is only correct if the SELECT is guaranteed to be in the same session as the INSERT, since currval is only defined after an INSERT. If the INSERT -> SELECT sequence is not done in the same session, the user will either get an error like PostgresDialect: select currval('<table name>_id_seq') did not return a row or, worse, an incorrect value for the ID.

Why did this look like it works?

Under the hood, Go's database/sql package has a connection pool with exactly 2 connections. These are managed with a stack-like data structure; the connection most recently returned to the pool will be the one used next. So under low load, events will happen in the following sequence:

  • gorp grabs the next database connection; it's connection 1.
  • gorp runs the INSERT query on connection 1.
  • gorp returns connection 1 to the pool.
  • gorp grabs the next database connection; it's connection 1.
  • gorp runs the SELECT currval query on connection 1. This works, because connection 1 still has the same session as it did when it executed the insert.
  • gorp returns connection 1 to the pool.

However, under high enough load, this won't work. Instead, this will happen:

  • gorp grabs the next database connection; it's connection 1.
  • gorp runs the INSERT query on connection 1.
  • gorp returns connection 1 to the pool.
  • Another piece of the application grabs the next database connection; it's connection 1.
  • gorp grabs the next database connection; it's connection 2.
  • gorp runs the SELECT currval query on connection 2. It fails, since currval is not defined for this session, or worse, it succeeds with the wrong ID (ie get's the ID from an older INSERT).

This is totally broken. In the worst case, it can return the wrong ID up to the application, where it could potentially be used as a foreign key, which would completely corrupt the database.

The fix

The idiomatic way to get the functionality we need here (returning the new ID up to the application) is with an INSERT ... RETURNING query. You can see an example of this here: https://github.com/eaigner/hood/blob/master/postgres.go#L64

At Braintree we forked gorp internally and hacked in our own implementation of this, but it looks like generalizing this fix takes significant work. We will try to work on a pull request, but in the mean time we wanted to raise the issue here to prevent other developers from getting burned by this problem.

This is the same issue as https://github.com/coopernurse/gorp/issues/19

LastInsertId in postgresql

I use grop insert a record into postgresql with postgresql driver pq,but i get an error when call
LastInsertId.The error like this

pq: R:"RangeVarGetRelidExtended" S:"ERROR" F:"namespace.c" L:"407" P:"16" M:"relation "invoice_test_id_seq" does not exist" C:"42P01"

I found that the invoice_test_id_seq dons't exist.

note:i use grop.CreateTable() create table to database.

Sql sort does not get reflected in the resulting slice in certain cases.

If this is not the right place to file this please excuse me, I couldn't find a mailing list or something.

I'm using Gorp on top of Postgres. I'm seeing an issue where, if the query is constructed a certain way, the ORDER BY clause does not get reflected in the resulting slice.

Here's the following code to exemplify the bug:

DbMap = &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
DbMap.AddTableWithName(Order{}, "orders").SetKeys(true, "Id")

// In this case, it works just fine.
var orders []*Order
_, err := DbMap.Select(&orders, "SELECT * FROM orders ORDER BY id ASC")

// But if I construct the query in a different way, 
// the order of the resulting slice is incorrect.
field := "id"
_, err := DbMap.Select(&orders, "SELECT * FROM orders ORDER BY $1 ASC", field)

Any ideas about why this might happen?

invalid memory address

Hi!

I have problems using gorp because when i connect to database and insert one row, the app just crash. My program gives this error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x68199]

goroutine 1 [running]:
sync/atomic.CompareAndSwapUint32(0x20, 0x100000000, 0x200000000, 0x159ac, 0x198df, ...)
    /private/tmp/bindist454984655/go/src/pkg/sync/atomic/asm_amd64.s:12 +0xd
sync.(*Mutex).Lock(0x20, 0x200000001)
    /usr/local/go/src/pkg/sync/mutex.go:40 +0x35
database/sql.(*DB).conn(0x0, 0x17abb, 0x70ff8, 0x42179270)
    /usr/local/go/src/pkg/database/sql/sql.go:236 +0x2d
database/sql.(*DB).exec(0x0, 0x42178b40, 0x25, 0x421792c0, 0x100000001, ...)
    /usr/local/go/src/pkg/database/sql/sql.go:344 +0x65
database/sql.(*DB).Exec(0x0, 0x42178b40, 0x100000025, 0x42179280, 0x100000001, ...)
    /usr/local/go/src/pkg/database/sql/sql.go:335 +0x124
github.com/coopernurse/gorp.(*DbMap).Exec(0x421a5000, 0x42178b40, 0x25, 0x42179280, 0x100000001, ...)
    /usr/local/go/src/pkg/github.com/coopernurse/gorp/gorp.go:713 +0x105
github.com/coopernurse/gorp.insert(0x421a5000, 0x421a7000, 0x421a5000, 0x44218af68, 0x100000001, ...)
    /usr/local/go/src/pkg/github.com/coopernurse/gorp/gorp.go:1063 +0x2e0
github.com/coopernurse/gorp.(*DbMap).Insert(0x421a5000, 0x44218af68, 0x100000001, 0x421774a0, 0x421a6000, ...)
    /usr/local/go/src/pkg/github.com/coopernurse/gorp/gorp.go:636 +0x6e
main.probarGORP()
    /Users/micho/Proyectos/DiagnosticoPDS-server-go/main.go:25 +0x303
main.main()
    /Users/micho/Proyectos/DiagnosticoPDS-server-go/main.go:9 +0x18

goroutine 2 [syscall]:
created by runtime.main
    /private/tmp/bindist454984655/go/src/pkg/runtime/proc.c:221

when i run this code

package main

import (
    "database/sql"
    "github.com/coopernurse/gorp"
)

func main() {
    probarGORP()
}

func probarGORP() {
    // construct a gorp DbMap
    //dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
    db, err := sql.Open("mysql", "pds:123456@localhost:3306/pds")
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
    table := dbmap.AddTableWithName(Sexo{}, "Sexo").SetKeys(true, "Id")
    table.ColMap("Id").Rename("id")
    table.ColMap("Nombre").Rename("nombre")
    p1 := &Sexo{3, "otro"}
    if err == nil || p1 == nil {

    }

    dbmap.Insert(p1) // Version is now 1
}

type Sexo struct {
    Id     int64
    Nombre string
}

The Mysql table is this:

CREATE TABLE IF NOT EXISTS `Sexo` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `nombre` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

My setup is a Mountain Lion macbook pro using go1.0.2. Is this a gorp issue ? How should i debug this ? I googled and it seems that "sync/atomic/asm_amd64.s:12" appears often in bugs.

thanks

[Feature Request] Select a single row

I have been using the Select family of methods for basically everything. One limitation is that selecting a single row is a bit awkward, since we have to select into a slice (or a value).

It would be nice to be able to do this:

var result struct {
  Foo int
  Bar string
}
dbm.Select(&result, "select foo, bar from baz where id = ?", id) 

Potentially, that could also unify the SelectVal functions as well. Instead of SelectInt, SelectStr, etc, developers could always use Select:

var result int64 
dbm.Select(&result, "..")

It may make sense to have separate singular and plural forms, where the singular form panics if more than 1 row comes back (or maybe that should be an error?)

var result Foo
dbm.SelectOne(&result, "...")

Thoughts?

Field insertion for Select

One thing that seems like it could become problematic is:
http://godoc.org/github.com/coopernurse/gorp#DbMap.Select

"Returns an error if one or more columns in the result do not match"

I think the common case will be someone writing

type Model struct {
  // many fields
}
dbm.Select(&models, "SELECT * FROM models WHERE ...")

Presently, as soon as a field is added to the table, the Select will start returning errors. This requires the developer to type out the entire field list for every select to be correct, otherwise there is no way to alter the table without trouble.

Two solutions could be:

  • Don't return an error if you don't have a place for a returned column (bad, but easy)
  • Provide a syntax for inserting the list of fields that you expect (better)

The latter could work like this:

dbm.Select(&models, "SELECT ... FROM models")

or

dbm.Select(&models, "SELECT {} FROM models")

and that could be replaced by the list of fields expected in type Model.

Parameter support for "IN (...)"

Presently I have to manually muck around with the SQL for queries that involve an IN. It would be nice if this worked:

select * from foo where id in (?)

where the corresponding parameter is a slice.

Since slices are not allowed as parameters, the implementation for this could avoid touching the normal flow, and it could only activate when a slice parameter is detected.

I think it would have to iterate through the parameter placeholders in the query -- when it gets to the one corresponding to the slice, it would have to expand it to the length of the slice, e.g. (?, ?, ?). Since some DBs have numbered parameters, it would also have to renumber all subsequent parameter placeholders.

Only thing is that the implementation seems a little bit challenging -- for example, is it safe to assume any '?' is a placeholder, regardless of context? Maybe not. Would need a full test suite.

To share tables with multiple dbmap

I want to create a connection pool ,it has many dbmap,and eache dbmap has a new connection. the dbmap has the same tables. so , some dbmaps should share they tables.but DbMap don't has dbmap.SetTables([]*TableMap) function to support do it.

I initialize the first dbmap1 ,and create tables ( use AddTableWithName() and CreateTables()),when I creat the next dbmap2,I do not neet to create tables,and

    dbmap1 := &DbMap{Db: connect(), Dialect: dialect}
dbmap2 := &DbMap{Db: connect(), Dialect: dialect}
dbmap3 := &DbMap{Db: connect(), Dialect: dialect}

tables := new([]*TableMap)

dbmap1.SetTables(tables)
dbmap2.SetTables(tables)
dbmap3.SetTables(tables)

hpgood@9b1aa8f
hpgood@f19396d

implicit assignment of unexported field 'suffix' in gorp.SqliteDialect literal

type Jack struct {
Id rune
Created int64
Updated int64
Nom string
Email string
Skill_Jack_Id []rune
Carac_Jack_Id []rune
Item_Jack_Id []rune
Skill_Jack_Private_Id []rune
Item_Jack_Private_Id []rune
Password string
Key_public string
Key_private string
Message_Id []rune
Statut string
Avatar string
}

func initdb() {
db, err := sql.Open("sqlite3", "foo.db")

// construct a gorp DbMap
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{"InnoDB", "UTF8"}}
t1 := dbmap.AddTableWithName(Jack{}, "Jack_test").SetKeys(true, "Id")

}

with https://github.com/mattn/go-sqlite3 it gaves me ::

command-line-arguments

./Jackpoint.go:166: implicit assignment of unexported field 'suffix' in gorp.SqliteDialect literal
./Jackpoint.go:166: too many values in struct initializer

Regards

Problem with #61 and time.Time arguments

I have a query that looks like this:

halfhourago := time.Now().UTC().Add(-30 * time.Minute)
var caseIds []int64
_, err := controllers.Dbm.Select(&caseIds,
    "SELECT id FROM cases WHERE status = 1 AND created < $1", halfhourago)

This fails after 071ca90 with a message like this:
pq: S:"ERROR" C:"42P02" M:"there is no parameter $1" P:"53" F:"parse_expr.c" L:"822" R:"transformParamRef" and the trace looks like this:
[gorp] SELECT id FROM cases WHERE status = 1 AND created < $1 [[]]
As you can see there is no argument.

I think the problem is maybeExpandNamedQuery which sees the time argument as a struct and tries to run expandNamedQuery.

Should I rewrite my query or should maybeExpandNamedQuery be modified to recognise time.Time?

Custom types using tags and serialization

In Go a string is basically an immutable []byte. This means a string type can contain binary data. This is not the same thing as CHAR, VARCHAR, or TEXT. In many cases it would be preferable to use BINARY, VARBINARY or BLOB.

There is a pull request that wants to use tags to indicate primary key and auto increment: #26

This is great, but I would also like to have types + lengths in mind as well.

Here's a small sample use-case:

type Field struct {
    Token string `db:",bytes:=16,primary"` // BINARY(16)
    Data []byte `db:",bytes"` // BLOB
}

The tag format would essentially be this:

db:"[name],[type[:[=]length]],[primary/autoincrement/unique/index]"

Notes:

  • "=" is used to indicate CHAR/BINARY verses VARCHAR/VARBINARY
  • Commas are required for specifying an nth argument. So for specifying "primary" and not "name" or "type" you would do ",,primary".
  • A panic would happen if "autoincrement" was specified for a non-int type.

Types would resemble Go types:

  • bytes ([]byte -> binary/varbinary/blob)
  • string (-> char/varchar/text)
  • int

Custom type conversion (structs, etc...) would be handled similar to encoding/json's type Marshaler interface and type Unmarshaler interface:

type IntMarshaler {
    MarshalGorpInt() (int64, error)
}
type IntUnmarshaler {
    UnmarshalGorpInt(int64) error
}
type StringMarshaler {
    MarshalGorpInt() (string, error)
}
type StringUnmarshaler {
    UnmarshalGorpString(string) error
}
type BytesMarshaler {
    MarshalGorpBytes() ([]bytes, error)
}
type BytesUnmarshaler {
    UnmarshalGorpBytes([]bytes) error
}
// .... other types
// Just two interfaces might be easier:
type Marshaler {
    MarshalGorp() (interface{}, error)
}
type Unmarshaler {
    UnmarshalGorp(interface{}) error
}

The type of Marshaler/Unmarshaler would correspond to the tagged type. The go type in the struct would have to implement the corresponding Marshaler/Unmarshaler. If it does not and the primitive type is supported, then no serialization is done. Other types that aren't normally supported would panic without a Marshaler, as it does now.

Example:

type Data struct {
// ... stuff
}

type Field struct {
    Token string `db:",bytes:=16,primary"` // BINARY(16)
    Data `db:",bytes"` // BLOB
}

func (d *Data) MarshalGorpBytes() ([]bytes, error) {
// Encode with encoding/gob
}

func (d *Data) UnmarshalGorpBytes([]bytes) error {
// Decode with encoding/gob
}

CreateAllTables generates incorrect SQL when using uint32 as primary key

If a struct has an unsigned ID, then CreateAllTables generates incorrect SQL. E.g.

type Person struct {
    Id               uint32
    Name             string
    Created          int64
    Updated          int64
}

The trace statement says

gorptest: 20:28:16.303405 create table if not exists `person` (`Id` varchar(255) not null primary key auto_increment, `Name` varchar(255), `Created` bigint, `Updated` bigint)  engine=InnoDB charset=UTF8; [[]]

So a varchar(255) has been incorrectly substituted instead of the 'int(10) unsigned' that would be expected.

The same problem applies with uint and uint64 too.

It also seems to happen with all uint, uint32 or uint64 fields, regardless of whether they are primary keys.

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.