// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Package db provides a single interface for interacting with different data // sources through the use of adapters that wrap well-known database drivers. // // As of today, `upper.io/db.v2` fully supports MySQL, PostgreSQL and SQLite (CRUD // + Transactions) and provides partial support for MongoDB and QL (CRUD only). // // Usage: // // import( // // Main package. // "upper.io/db.v2" // // PostgreSQL adapter. // "upper.io/db.v2/postgresql" // ) // // `upper.io/db.v2` is not an ORM and thus does not impose any hard restrictions // on data structures: // // // This code works the same for all supported databases. // var people []Person // res = col.Find(db.Cond{"name": "Max"}).Limit(2).Sort("-input") // err = res.All(&people) package db // import "upper.io/db.v2" import ( "fmt" "reflect" ) // Constraint interface represents a condition. type Constraint interface { Key() interface{} Value() interface{} } // Constraints interface provides the Constraints() method. type Constraints interface { Constraints() []Constraint } // Compound represents a compound statement created by joining constraints. type Compound interface { Sentences() []Compound Operator() CompoundOperator } // CompoundOperator represents the operator of a compound. type CompoundOperator uint // Compound operators. const ( OperatorNone = CompoundOperator(iota) OperatorAnd OperatorOr ) // RawValue interface represents values that can bypass SQL filters. Use with // care. type RawValue interface { fmt.Stringer } // Function interface defines methods for representing database functions. type Function interface { Arguments() []interface{} Name() string } // Marshaler is the interface implemented by structs that can marshal // themselves into data suitable for storage. type Marshaler interface { MarshalDB() (interface{}, error) } // Unmarshaler is the interface implemented by structs that can transform // themselves from storage data into a valid value. type Unmarshaler interface { UnmarshalDB(interface{}) error } // Cond is a map that defines conditions that can be passed to // `db.Collection.Find()` and `db.Result.Where()`. // // Each entry of the map represents a condition (a column-value relation bound // by a comparison operator). The comparison operator is optional and can be // specified after the column name, if no comparison operator is provided the // equality is used. // // Examples: // // // Where age equals 18. // db.Cond{"age": 18} // // // Where age is greater than or equal to 18. // db.Cond{"age >=": 18} // // // Where id is in a list of ids. // db.Cond{"id IN": []{1, 2, 3}} // // // Where age is lower than 18 (mongodb-like operator). // db.Cond{"age $lt": 18} // // // Where age > 32 and age < 35 // db.Cond{"age >": 32, "age <": 35} type Cond map[interface{}]interface{} // Constraints returns each one of the map records as a constraint. func (c Cond) Constraints() []Constraint { z := make([]Constraint, 0, len(c)) for k, v := range c { z = append(z, NewConstraint(k, v)) } return z } // Sentences returns each one of the map records as a compound. func (c Cond) Sentences() []Compound { z := make([]Compound, 0, len(c)) for k, v := range c { z = append(z, Cond{k: v}) } return z } // Operator returns the default compound operator. func (c Cond) Operator() CompoundOperator { return OperatorNone } // rawValue implements RawValue type rawValue struct { v string } func (r rawValue) String() string { return r.v } type compound struct { conds []Compound } func newCompound(c ...Compound) *compound { return &compound{c} } func (c *compound) Sentences() []Compound { return c.conds } func (c *compound) Operator() CompoundOperator { return OperatorNone } func (c *compound) push(a ...Compound) *compound { c.conds = append(c.conds, a...) return c } // Union represents a compound joined by OR. type Union struct { *compound } // Or adds more terms to the compound. func (o *Union) Or(conds ...Compound) *Union { o.compound.push(conds...) return o } // Operator returns the OR operator. func (o *Union) Operator() CompoundOperator { return OperatorOr } // And adds more terms to the compound. func (a *Intersection) And(conds ...Compound) *Intersection { a.compound.push(conds...) return a } // Intersection represents a compound joined by AND. type Intersection struct { *compound } // Operator returns the AND operator. func (a *Intersection) Operator() CompoundOperator { return OperatorAnd } type constraint struct { k interface{} v interface{} } func (c constraint) Key() interface{} { return c.k } func (c constraint) Value() interface{} { return c.v } // NewConstraint creates a condition func NewConstraint(key interface{}, value interface{}) Constraint { return constraint{k: key, v: value} } // Func represents a database function. // // Examples: // // // MOD(29, 9) // db.Func("MOD", 29, 9) // // // CONCAT("foo", "bar") // db.Func("CONCAT", "foo", "bar") // // // NOW() // db.Func("NOW") // // // RTRIM("Hello ") // db.Func("RTRIM", "Hello ") func Func(name string, args ...interface{}) Function { if len(args) == 1 { if reflect.TypeOf(args[0]).Kind() == reflect.Slice { iargs := make([]interface{}, len(args)) for i := range args { iargs[i] = args[i] } args = iargs } } return &dbFunc{name: name, args: args} } type dbFunc struct { name string args []interface{} } func (f *dbFunc) Arguments() []interface{} { return f.args } func (f *dbFunc) Name() string { return f.name } // And joins conditions under logical conjunction. Conditions can be // represented by db.Cond{}, db.Or() or db.And(). // // Examples: // // // name = "Peter" AND last_name = "Parker" // db.And( // db.Cond{"name": "Peter"}, // db.Cond{"last_name": "Parker "}, // ) // // // (name = "Peter" OR name = "Mickey") AND last_name = "Mouse" // db.And( // db.Or( // db.Cond{"name": "Peter"}, // db.Cond{"name": "Mickey"}, // ), // db.Cond{"last_name": "Mouse"}, // ) func And(conds ...Compound) *Intersection { return &Intersection{compound: newCompound(conds...)} } // Or joins conditions under logical disjunction. Conditions can be represented // by db.Cond{}, db.Or() or db.And(). // // Example: // // // year = 2012 OR year = 1987 // db.Or( // db.Cond{"year": 2012}, // db.Cond{"year": 1987}, // ) func Or(conds ...Compound) *Union { return &Union{compound: newCompound(conds...)} } // Raw marks chunks of data as protected, so they pass directly to the query // without any filtering. Use with care. // // Example: // // // SOUNDEX('Hello') // Raw("SOUNDEX('Hello')") func Raw(s string) RawValue { return rawValue{v: s} } // Database is an interface that defines methods that must be satisfied by // database adapters. type Database interface { // Driver returns the underlying driver the wrapper uses. // // In order to actually use the driver the `interface{}` value has to be // casted to the appropriate type. // // Example: // internalSQLDriver := sess.Driver().(*sql.DB) Driver() interface{} // Open attempts to stablish a connection with a database manager. Open(ConnectionURL) error // Clone duplicates the current database session. Returns an error if the // clone did not succeed. // Clone() (Database, error) // Ping returns an error if the database manager cannot be reached. Ping() error // Close closes the currently active connection to the database. Close() error // Collection returns a Collection given a table name. The collection may or // may not exists and that could be an error when querying depending on the // database you're working with, MongoDB does not care but SQL databases do // care. If you want to know if a Collection exists use the Exists() method // on a Collection. Collection(string) Collection // Collections returns the names of all non-system tables on the database. Collections() ([]string, error) // Name returns the name of the active database. Name() string ConnectionURL() ConnectionURL } // Tx is an interface that enhaces the `Database` interface with additional // methods for transactions. // // Example: // // [...] // if sess, err = db.Open(postgresql.Adapter, settings); err != nil { // log.Fatal(err) // } // // var tx db.Tx // if tx, err = sess.NewTransaction(); err != nil { // log.Fatal(err) // } // // var artist db.Collection // if artist, err = tx.Collection("artist"); err != nil { // log.Fatal(err) // } // // [...] type Tx interface { Database // Rollback discards all the instructions on the current transaction. Rollback() error // Commit commits the current transactions. Commit() error } // Collection is an interface that defines methods useful for handling data // sources or tables. type Collection interface { // Insert inserts a new item into the collection, it accepts a map or a // struct as argument and returns the ID of the newly added element. The type // of this ID depends on the database adapter. The ID returned by Insert() // can be passed directly to Find() to find the recently added element. // // Insert does not alter the passed element. Insert(interface{}) (interface{}, error) // InsertReturning is like Insert() but it updates the passed pointer to map // or struct with the newly inserted element. This is all done atomically // within a transaction. If the database does not support transactions this // method returns db.ErrUnsupported. InsertReturning(interface{}) error // Exists returns true if the collection exists. Exists() bool // Find returns a result set with the given filters. Find(...interface{}) Result // Truncate removes all elements on the collection and resets its IDs. Truncate() error // Name returns the name of the collection. Name() string } // Result is an interface that defines methods useful for working with result // sets. type Result interface { // Limit defines the maximum number of results in this set. It only has // effect on `One()`, `All()` and `Next()`. Limit(int) Result // Offset ignores the first *n* results. It only has effect on `One()`, `All()` // and `Next()`. Offset(int) Result // OrderBy receives field names that define the order in which elements will be // returned in a query, field names may be prefixed with a minus sign (-) // indicating descending order, ascending order will be used otherwise. OrderBy(...interface{}) Result // Select defines specific columns to be returned from the elements of the // set. Select(...interface{}) Result // Where discards the initial filtering conditions and sets new ones. Where(...interface{}) Result // Group is used to group results that have the same value in the same column // or columns. Group(...interface{}) Result // Delete deletes all items within the result set. `Offset()` and `Limit()` are // not honoured by `Delete()`. Delete() error // Update modifies all items within the result set. `Offset()` and `Limit()` // are not honoured by `Update()`. Update(interface{}) error // Count returns the number of items that match the set conditions. `Offset()` // and `Limit()` are not honoured by `Count()` Count() (uint64, error) // Next fetches the next result within the result set and dumps it into the // given pointer to struct or pointer to map. You must manually call // `Close()` after finishing using `Next()`. Next(interface{}) error // One fetches the first result within the result set and dumps it into the // given pointer to struct or pointer to map. The result set is automatically // closed after picking the element, so there is no need to call `Close()` // manually. One(interface{}) error // All fetches all results within the result set and dumps them into the // given pointer to slice of maps or structs. The result set is // automatically closed, so there is no need to call `Close()` manually. All(interface{}) error // Close closes the result set. Close() error } // ConnectionURL represents a connection string type ConnectionURL interface { // String returns the connection string that is going to be passed to the // adapter. String() string } // EnvEnableDebug can be used by adapters to determine if the user has enabled // debugging. // // If the user sets the `UPPERIO_DB_DEBUG` environment variable to a // non-empty value, all generated statements will be printed at runtime to // the standard logger. // // Example: // // UPPERIO_DB_DEBUG=1 go test // // UPPERIO_DB_DEBUG=1 ./go-program const EnvEnableDebug = `UPPERIO_DB_DEBUG` var ( _ = Function(&dbFunc{}) _ = Constraints(Cond{}) _ = Compound(Cond{}) _ = Constraint(&constraint{}) _ = RawValue(&rawValue{}) )