From 69b156d7d5a3dfdf8bf0af9adc7c3d58bc03c90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net> Date: Sun, 20 Mar 2016 12:21:16 -0600 Subject: [PATCH] Changing Collection() signature. Closes #148 --- db.go | 15 ++-- db_test.go | 42 +-------- internal/schema/schema.go | 82 +++++++++++------- internal/sqladapter/database.go | 36 ++------ mongo/benchmark_test.go | 63 ++------------ mongo/database.go | 21 +---- mongo/database_test.go | 63 +++----------- mysql/benchmark_test.go | 98 +++------------------ mysql/database.go | 125 ++++++++------------------- mysql/database_test.go | 81 +++++------------ postgresql/benchmark_test.go | 100 +++------------------ postgresql/database.go | 127 ++++++++------------------- postgresql/database_test.go | 132 +++++++--------------------- ql/benchmark_test.go | 98 +++------------------ ql/database.go | 93 +++++--------------- ql/database_test.go | 148 +++++--------------------------- sqlite/benchmark_test.go | 98 +++------------------ sqlite/database.go | 134 +++++++++++------------------ sqlite/database_test.go | 81 +++++------------ 19 files changed, 370 insertions(+), 1267 deletions(-) diff --git a/db.go b/db.go index 216d1927..961b37f9 100644 --- a/db.go +++ b/db.go @@ -193,15 +193,12 @@ type Database interface { // Close closes the currently active connection to the database. Close() error - // C is a short-hand for `Collection()`. If the given collection does not - // exists subsequent calls to any `Collection{}` or `Result{}` method that - // expect the collection to exists will fail returning the original error a - // call to `Collection()` would have returned. The output of `C()` may be a - // cached collection value. - C(string) Collection - - // Collection returns a `Collection{}` given a table name. - Collection(string) (Collection, 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) diff --git a/db_test.go b/db_test.go index ad9692ba..fdaac5b1 100644 --- a/db_test.go +++ b/db_test.go @@ -549,18 +549,9 @@ func TestSimpleCRUD(t *testing.T) { BornUT: timeType{born}, } - col, err := sess.Collection(`birthdays`) - - if err != nil { - if wrapper == `mongo` && err == db.ErrCollectionDoesNotExist { - // Expected error with mongodb. - } else { - t.Fatalf(`Could not use collection with wrapper %s: %q`, wrapper, err) - } - } + col := sess.Collection(`birthdays`) var id interface{} - if id, err = col.Append(controlItem); err != nil { t.Fatalf(`Could not append item with wrapper %s: %q`, wrapper, err) } @@ -693,16 +684,7 @@ func TestFibonacci(t *testing.T) { } defer sess.Close() - var col db.Collection - col, err = sess.Collection("fibonacci") - - if err != nil { - if wrapper == `mongo` && err == db.ErrCollectionDoesNotExist { - // Expected error with mongodb. - } else { - t.Fatalf(`Could not use collection with wrapper %s: %q`, wrapper, err) - } - } + col := sess.Collection("fibonacci") // Adding some items. var i uint64 @@ -962,16 +944,7 @@ func TestEven(t *testing.T) { } defer sess.Close() - var col db.Collection - col, err = sess.Collection("is_even") - - if err != nil { - if wrapper == `mongo` && err == db.ErrCollectionDoesNotExist { - // Expected error with mongodb. - } else { - t.Fatalf(`Could not use collection with wrapper %s: %q`, wrapper, err) - } - } + col := sess.Collection("is_even") // Adding some items. var i int @@ -1067,7 +1040,6 @@ func TestEven(t *testing.T) { func TestExplicitAndDefaultMapping(t *testing.T) { var err error - var col db.Collection var sess db.Database var res db.Result @@ -1087,13 +1059,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) { defer sess.Close() - if col, err = sess.Collection("CaSe_TesT"); err != nil { - if wrapper == `mongo` && err == db.ErrCollectionDoesNotExist { - // Nothing, this is expected. - } else { - t.Fatal(err) - } - } + col := sess.Collection("CaSe_TesT") if err = col.Truncate(); err != nil { if wrapper == `mongo` { diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 76272006..3293466e 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -22,52 +22,74 @@ // Package schema provides basic information on relational database schemas. package schema +import ( + "sync" +) + // DatabaseSchema represents a collection of tables. type DatabaseSchema struct { - Name string - Alias string - Tables []string - TableInfo map[string]*TableSchema + name string + tables map[string]*TableSchema + + mu sync.RWMutex } // TableSchema represents a single table. type TableSchema struct { - PrimaryKey []string - Alias string - Columns []string + pk []string + + mu sync.RWMutex } // NewDatabaseSchema creates and returns a database schema. func NewDatabaseSchema() *DatabaseSchema { - schema := new(DatabaseSchema) - schema.Tables = []string{} - schema.TableInfo = make(map[string]*TableSchema) - return schema + s := &DatabaseSchema{ + tables: make(map[string]*TableSchema), + } + return s } -// AddTable adds a table into the database schema. If the table does not exists -// it is created before being added. -func (d *DatabaseSchema) AddTable(name string) { - if _, ok := d.TableInfo[name]; !ok { - table := new(TableSchema) - table.PrimaryKey = []string{} - table.Columns = []string{} - d.TableInfo[name] = table - d.Tables = append(d.Tables, name) - } +// Name returns the name of the database. +func (s *DatabaseSchema) Name() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.name +} + +// Set name sets the name of the database. +func (s *DatabaseSchema) SetName(name string) { + s.mu.Lock() + defer s.mu.Unlock() + s.name = name } // Table retrives a table from the schema. -func (d *DatabaseSchema) Table(name string) *TableSchema { - d.AddTable(name) - return d.TableInfo[name] +func (s *DatabaseSchema) Table(name string) *TableSchema { + s.mu.Lock() + defer s.mu.Unlock() + + if _, ok := s.tables[name]; !ok { + s.tables[name] = &TableSchema{} + } + + return s.tables[name] } -// HasTable returns true if the given table is already defined within the -// schema. -func (d *DatabaseSchema) HasTable(name string) bool { - if _, ok := d.TableInfo[name]; ok { - return true +func (t *TableSchema) SetPrimaryKeys(pk []string) { + t.mu.Lock() + defer t.mu.Unlock() + + if len(pk) == 0 { + t.pk = []string{} // if nil or empty array + return } - return false + + t.pk = pk +} + +func (t *TableSchema) PrimaryKeys() []string { + t.mu.RLock() + defer t.mu.RUnlock() + + return t.pk } diff --git a/internal/sqladapter/database.go b/internal/sqladapter/database.go index 8332b439..094145bb 100644 --- a/internal/sqladapter/database.go +++ b/internal/sqladapter/database.go @@ -10,7 +10,6 @@ import ( "upper.io/db.v2/builder/cache" "upper.io/db.v2/builder/sqlbuilder" "upper.io/db.v2/builder/sqlgen" - "upper.io/db.v2/internal/adapter" "upper.io/db.v2/internal/debug" "upper.io/db.v2/internal/schema" "upper.io/db.v2/internal/sqlutil/tx" @@ -92,8 +91,9 @@ func (d *BaseDatabase) Tx() *sqltx.Tx { return d.tx } -func (d *BaseDatabase) NewSchema() { +func (d *BaseDatabase) NewSchema() *schema.DatabaseSchema { d.schema = schema.NewDatabaseSchema() + return d.schema } func (d *BaseDatabase) Schema() *schema.DatabaseSchema { @@ -146,39 +146,19 @@ func (d *BaseDatabase) Close() error { return nil } -// C returns a collection interface. -func (d *BaseDatabase) C(name string) db.Collection { +// Collection returns a Collection given a name. +func (d *BaseDatabase) Collection(name string) db.Collection { + d.collectionsMu.Lock() if c, ok := d.collections[name]; ok { + d.collectionsMu.Unlock() return c } - c, err := d.Collection(name) - if err != nil { - return &adapter.NonExistentCollection{Err: err} - } - - return c -} - -// Collection returns the table that matches the given name. -func (d *BaseDatabase) Collection(name string) (db.Collection, error) { - if d.tx != nil { - if d.tx.Done() { - return nil, sql.ErrTxDone - } - } - - if err := d.partial.TableExists(name); err != nil { - return nil, err - } - col := d.partial.NewTable(name) - - d.collectionsMu.Lock() d.collections[name] = col d.collectionsMu.Unlock() - return col, nil + return col } func (d *BaseDatabase) ConnectionURL() db.ConnectionURL { @@ -187,7 +167,7 @@ func (d *BaseDatabase) ConnectionURL() db.ConnectionURL { // Name returns the name of the database. func (d *BaseDatabase) Name() string { - return d.schema.Name + return d.schema.Name() } // Exec compiles and executes a statement that does not return any rows. diff --git a/mongo/benchmark_test.go b/mongo/benchmark_test.go index c15d72da..ca6d3a81 100644 --- a/mongo/benchmark_test.go +++ b/mongo/benchmark_test.go @@ -80,10 +80,7 @@ func BenchmarkUpperAppend(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -110,10 +107,7 @@ func BenchmarkUpperAppendVariableArgs(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -193,10 +187,7 @@ func BenchmarkUpperFind(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `bson:"name"` @@ -213,32 +204,6 @@ func BenchmarkUpperFind(b *testing.B) { } } -// BenchmarkUpperFindWithC benchmarks upper.io/db.v2's One method. -func BenchmarkUpperFindWithC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = connectAndAddFakeRows(); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - type artistType struct { - Name string `db:"name"` - } - - var item artistType - - b.ResetTimer() - for i := 0; i < b.N; i++ { - res := sess.C("artist").Find(db.Cond{"name": artistN(i)}) - if err = res.One(&item); err != nil { - b.Fatal(err) - } - } -} - // BenchmarkUpperFindAll benchmarks upper.io/db.v2's All method. func BenchmarkUpperFindAll(b *testing.B) { var err error @@ -250,10 +215,7 @@ func BenchmarkUpperFindAll(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `bson:"name"` @@ -309,10 +271,7 @@ func BenchmarkUpperUpdate(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `bson:"name"` @@ -362,10 +321,7 @@ func BenchmarkUpperRemove(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -389,10 +345,7 @@ func BenchmarkUpperGetCollection(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + sess.Collection("artist") } } @@ -409,6 +362,6 @@ func BenchmarkUpperC(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - sess.C("artist") + sess.Collection("artist") } } diff --git a/mongo/database.go b/mongo/database.go index 55ab52a3..44d61e4b 100644 --- a/mongo/database.go +++ b/mongo/database.go @@ -156,21 +156,8 @@ func (s *Source) Collections() (cols []string, err error) { return cols, nil } -// C returns a collection interface. -func (s *Source) C(name string) db.Collection { - if col, ok := s.collections[name]; ok { - // We can safely ignore if the collection exists or not. - return col - } - - c, _ := s.Collection(name) - return c -} - // Collection returns a collection by name. -func (s *Source) Collection(name string) (db.Collection, error) { - var err error - +func (s *Source) Collection(name string) db.Collection { s.collectionsMu.Lock() defer s.collectionsMu.Unlock() @@ -185,11 +172,7 @@ func (s *Source) Collection(name string) (db.Collection, error) { s.collections[name] = col } - if !col.Exists() { - err = db.ErrCollectionDoesNotExist - } - - return col, err + return col } func (s *Source) versionAtLeast(version ...int) bool { diff --git a/mongo/database_test.go b/mongo/database_test.go index 3bf44d6f..93e297db 100644 --- a/mongo/database_test.go +++ b/mongo/database_test.go @@ -239,10 +239,7 @@ func TestTruncate(t *testing.T) { for _, name := range collections { // Pointing the collection. - col, err := sess.Collection(name) - if err != nil { - t.Fatal(err) - } + col := sess.Collection(name) // The collection may ot may not exists. exists := col.Exists() @@ -276,14 +273,7 @@ func TestAppend(t *testing.T) { defer sess.Close() // Getting a pointer to the "artist" collection. - artist, err := sess.Collection("artist") - - if err != nil { - // We can use the collection even if it does not exists. - if err != db.ErrCollectionDoesNotExist { - t.Fatal(err) - } - } + artist := sess.Collection("artist") // Appending a map. id, err = artist.Append(map[string]string{ @@ -423,7 +413,7 @@ func TestResultCount(t *testing.T) { defer sess.Close() // We should close the database when it's no longer in use. - artist, _ := sess.Collection("artist") + artist := sess.Collection("artist") res = artist.Find() @@ -457,18 +447,10 @@ func TestGroup(t *testing.T) { defer sess.Close() - if stats, err = sess.Collection("statsTest"); err != nil { - if err != db.ErrCollectionDoesNotExist { - t.Fatal(err) - } - } + stats = sess.Collection("statsTest") // Truncating table. - if err == nil { - if err = stats.Truncate(); err != nil { - t.Fatal(err) - } - } + stats.Truncate() // Adding row append. for i := 0; i < 1000; i++ { @@ -512,7 +494,7 @@ func TestResultNonExistentCount(t *testing.T) { defer sess.Close() - total, err := sess.C("notartist").Find().Count() + total, err := sess.Collection("notartist").Find().Count() if err != nil { t.Fatal("MongoDB should not care about a non-existent collecton.", err) @@ -539,11 +521,7 @@ func TestResultFetch(t *testing.T) { // We should close the database when it's no longer in use. defer sess.Close() - artist, err := sess.Collection("artist") - - if err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Testing map res = artist.Find() @@ -709,11 +687,7 @@ func TestUpdate(t *testing.T) { defer sess.Close() // Getting a pointer to the "artist" collection. - artist, err := sess.Collection("artist") - - if err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Value value := struct { @@ -810,11 +784,7 @@ func TestOperators(t *testing.T) { defer sess.Close() // Getting a pointer to the "artist" collection. - artist, err := sess.Collection("artist") - - if err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") rowS := struct { ID uint64 @@ -846,11 +816,7 @@ func TestRemove(t *testing.T) { defer sess.Close() // Getting a pointer to the "artist" collection. - artist, err := sess.Collection("artist") - - if err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Getting the first artist. res := artist.Find(db.Cond{"_id $ne": nil}).Limit(1) @@ -889,11 +855,7 @@ func TestSetterAndConstrainer(t *testing.T) { defer sess.Close() - if compositeKeys, err = sess.Collection("composite_keys"); err != nil { - if err != db.ErrCollectionDoesNotExist { - t.Fatal(err) - } - } + compositeKeys = sess.Collection("composite_keys") //n := rand.Intn(100000) @@ -953,8 +915,7 @@ func TestDataTypes(t *testing.T) { defer sess.Close() // Getting a pointer to the "data_types" collection. - dataTypes, err := sess.Collection("data_types") - dataTypes.Truncate() + dataTypes := sess.Collection("data_types") // Appending our test subject. id, err := dataTypes.Append(testValues) diff --git a/mysql/benchmark_test.go b/mysql/benchmark_test.go index 0aa29074..17faf062 100644 --- a/mysql/benchmark_test.go +++ b/mysql/benchmark_test.go @@ -261,10 +261,7 @@ func BenchmarkUpperAppend(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -291,10 +288,7 @@ func BenchmarkUpperAppendVariableArgs(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -327,10 +321,7 @@ func BenchmarkUpperAppendTransaction(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -370,10 +361,7 @@ func BenchmarkUpperAppendTransactionWithMap(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -459,10 +447,7 @@ func BenchmarkUpperFind(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -479,32 +464,6 @@ func BenchmarkUpperFind(b *testing.B) { } } -// BenchmarkUpperFindWithC benchmarks upper.io/db.v2's One method. -func BenchmarkUpperFindWithC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = connectAndAddFakeRows(); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - type artistType struct { - Name string `db:"name"` - } - - var item artistType - - b.ResetTimer() - for i := 0; i < b.N; i++ { - res := sess.C("artist").Find(db.Cond{"name": artistN(i)}) - if err = res.One(&item); err != nil { - b.Fatal(err) - } - } -} - // BenchmarkUpperFindAll benchmarks upper.io/db.v2's All method. func BenchmarkUpperFindAll(b *testing.B) { var err error @@ -516,10 +475,7 @@ func BenchmarkUpperFindAll(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -601,10 +557,7 @@ func BenchmarkUpperUpdate(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -679,10 +632,7 @@ func BenchmarkUpperRemove(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -706,27 +656,7 @@ func BenchmarkUpperGetCollection(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkUpperC -func BenchmarkUpperC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - sess.C("artist") + sess.Collection("artist") } } @@ -749,10 +679,7 @@ func BenchmarkUpperCommitManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -793,10 +720,7 @@ func BenchmarkUpperRollbackManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) diff --git a/mysql/database.go b/mysql/database.go index 7da20f45..95485b09 100644 --- a/mysql/database.go +++ b/mysql/database.go @@ -27,9 +27,9 @@ import ( "database/sql" _ "github.com/go-sql-driver/mysql" // MySQL driver. + "upper.io/db.v2" "upper.io/db.v2/builder/sqlgen" template "upper.io/db.v2/builder/template/mysql" - "upper.io/db.v2" "upper.io/db.v2/internal/sqladapter" "upper.io/db.v2/internal/sqlutil/tx" ) @@ -107,31 +107,29 @@ func (d *database) NewTable(name string) db.Collection { // Collections returns a list of non-system tables from the database. func (d *database) Collections() (collections []string, err error) { - if len(d.Schema().Tables) == 0 { - q := d.Builder().Select("table_name"). - From("information_schema.tables"). - Where("table_schema = ?", d.Schema().Name) + q := d.Builder().Select("table_name"). + From("information_schema.tables"). + Where("table_schema = ?", d.Schema().Name()) - iter := q.Iterator() - defer iter.Close() + iter := q.Iterator() + defer iter.Close() - for iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { - return nil, err - } - d.Schema().AddTable(tableName) + for iter.Next() { + var tableName string + if err := iter.Scan(&tableName); err != nil { + return nil, err } + collections = append(collections, tableName) } - return d.Schema().Tables, nil + return collections, nil } // Drop removes all tables from the current database. func (d *database) Drop() error { stmt := &sqlgen.Statement{ Type: sqlgen.DropDatabase, - Database: sqlgen.DatabaseWithName(d.Schema().Name), + Database: sqlgen.DatabaseWithName(d.Schema().Name()), } if _, err := d.Builder().Exec(stmt); err != nil { return err @@ -166,105 +164,52 @@ func (d *database) Transaction() (db.Tx, error) { // PopulateSchema looks up for the table info in the database and populates its // schema for internal use. -func (d *database) PopulateSchema() (err error) { - var collections []string - - d.NewSchema() +func (d *database) PopulateSchema() error { + schema := d.NewSchema() q := d.Builder().Select(db.Raw("DATABASE() AS name")) - var dbName string - iter := q.Iterator() defer iter.Close() if iter.Next() { - if err := iter.Scan(&dbName); err != nil { - return err - } - } else { - return iter.Err() - } - - d.Schema().Name = dbName - - if collections, err = d.Collections(); err != nil { + var name string + err := iter.Scan(&name) + schema.SetName(name) return err } - - for i := range collections { - if _, err = d.Collection(collections[i]); err != nil { - return err - } - } - - return err + return iter.Err() } // TableExists checks whether a table exists and returns an error in case it doesn't. func (d *database) TableExists(name string) error { - if d.Schema().HasTable(name) { - return nil - } - q := d.Builder().Select("table_name"). From("information_schema.tables"). - Where("table_schema = ? AND table_name = ?", d.Schema().Name, name) + Where("table_schema = ? AND table_name = ?", d.Schema().Name(), name) iter := q.Iterator() defer iter.Close() if iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { + var name string + if err := iter.Scan(&name); err != nil { return err } - } else { - return db.ErrCollectionDoesNotExist - } - - return nil -} - -// TableColumns returns all columns from the given table. -func (d *database) TableColumns(tableName string) ([]string, error) { - s := d.Schema() - - if len(s.Table(tableName).Columns) == 0 { - - q := d.Builder().Select("column_name"). - From("information_schema.columns"). - Where("table_schema = ? AND table_name = ?", d.Schema().Name, tableName) - - var rows []struct { - Name string `db:"column_name"` - } - - if err := q.Iterator().All(&rows); err != nil { - return nil, err - } - - s.TableInfo[tableName].Columns = make([]string, 0, len(rows)) - - for i := range rows { - s.TableInfo[tableName].Columns = append(s.TableInfo[tableName].Columns, rows[i].Name) - } + return nil } - - return s.Table(tableName).Columns, nil + return db.ErrCollectionDoesNotExist } // TablePrimaryKey returns all primary keys from the given table. func (d *database) TablePrimaryKey(tableName string) ([]string, error) { - s := d.Schema() + tableSchema := d.Schema().Table(tableName) - ts := s.Table(tableName) - - if len(ts.PrimaryKey) != 0 { - return ts.PrimaryKey, nil + pk := tableSchema.PrimaryKeys() + if pk != nil { + return pk, nil } - ts.PrimaryKey = make([]string, 0, 1) + pk = []string{} q := d.Builder().Select("k.column_name"). From("information_schema.table_constraints AS t"). @@ -274,21 +219,23 @@ func (d *database) TablePrimaryKey(tableName string) ([]string, error) { t.constraint_type = 'primary key' AND t.table_schema = ? AND t.table_name = ? - `, d.Schema().Name, tableName). + `, d.Schema().Name(), tableName). OrderBy("k.ordinal_position") iter := q.Iterator() defer iter.Close() for iter.Next() { - var pKey string - if err := iter.Scan(&pKey); err != nil { + var k string + if err := iter.Scan(&k); err != nil { return nil, err } - ts.PrimaryKey = append(ts.PrimaryKey, pKey) + pk = append(pk, k) } - return ts.PrimaryKey, nil + tableSchema.SetPrimaryKeys(pk) + + return pk, nil } func (d *database) clone() (*database, error) { diff --git a/mysql/database_test.go b/mysql/database_test.go index fd901537..5bda572b 100644 --- a/mysql/database_test.go +++ b/mysql/database_test.go @@ -267,12 +267,10 @@ func TestTruncate(t *testing.T) { for _, name := range collections { // Getting a collection. - if col, err = sess.Collection(name); err != nil { - t.Fatal(err) - } + col = sess.Collection(name) // Table must exists before we can use it. - if col.Exists() == true { + if col.Exists() { // Truncating the table. if err = col.Truncate(); err != nil { t.Fatal(err) @@ -295,9 +293,7 @@ func TestAppend(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Attempt to append a map. itemMap := map[string]string{ @@ -389,9 +385,7 @@ func TestNullableFields(t *testing.T) { var test testType - if col, err = sess.Collection(`data_types`); err != nil { - t.Fatal(err) - } + col = sess.Collection(`data_types`) if err = col.Truncate(); err != nil { t.Fatal(err) @@ -473,9 +467,7 @@ func TestGroup(t *testing.T) { defer sess.Close() - if stats, err = sess.Collection("stats_test"); err != nil { - t.Fatal(err) - } + stats = sess.Collection("stats_test") // Truncating table. if err = stats.Truncate(); err != nil { @@ -523,9 +515,7 @@ func TestResultCount(t *testing.T) { defer sess.Close() // We should close the database when it's no longer in use. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining a set with no conditions. res = artist.Find() @@ -550,9 +540,9 @@ func TestResultNonExistentCount(t *testing.T) { defer sess.Close() - total, err := sess.C("notartist").Find().Count() + total, err := sess.Collection("notartist").Find().Count() - if err != db.ErrCollectionDoesNotExist { + if err == nil { t.Fatal("Expecting a specific error, got", err) } @@ -574,9 +564,7 @@ func TestResultFetch(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Dumping into a map. rowMap := map[string]interface{}{} @@ -707,9 +695,7 @@ func TestUpdate(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining destination struct value := struct { @@ -797,9 +783,7 @@ func TestFunction(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") rowStruct := struct { ID uint64 @@ -885,9 +869,7 @@ func TestRemove(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Getting the artist with id = 1 res = artist.Find(db.Cond{"id": 1}) @@ -920,10 +902,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { t.Fatal(err) @@ -939,7 +918,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Attempt to use the same transaction should fail. - if _, err = tx.Collection("artist"); err == nil { + if err = tx.Commit(); err == nil { t.Fatalf("Illegal, transaction has already been commited.") } @@ -950,9 +929,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -978,9 +955,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") var count uint64 if count, err = artist.Find().Count(); err != nil { @@ -998,9 +973,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -1022,9 +995,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -1041,9 +1012,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -1070,9 +1039,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we have 4 rows. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -1096,9 +1063,7 @@ func TestCompositeKeys(t *testing.T) { defer sess.Close() - if compositeKeys, err = sess.Collection("composite_keys"); err != nil { - t.Fatal(err) - } + compositeKeys = sess.Collection("composite_keys") n := rand.Intn(100000) @@ -1150,9 +1115,7 @@ func TestDataTypes(t *testing.T) { defer sess.Close() // Getting a pointer to the "data_types" collection. - if dataTypes, err = sess.Collection("data_types"); err != nil { - t.Fatal(err) - } + dataTypes = sess.Collection("data_types") // Removing all data. if err = dataTypes.Truncate(); err != nil { diff --git a/postgresql/benchmark_test.go b/postgresql/benchmark_test.go index d1fcab85..b05eda59 100644 --- a/postgresql/benchmark_test.go +++ b/postgresql/benchmark_test.go @@ -413,11 +413,7 @@ func BenchmarkUpperAppend(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - + artist := sess.Collection("artist") artist.Truncate() item := struct { @@ -443,11 +439,7 @@ func BenchmarkUpperAppendVariableArgs(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - + artist := sess.Collection("artist") artist.Truncate() b.ResetTimer() @@ -479,10 +471,7 @@ func BenchmarkUpperAppendTransaction(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -522,10 +511,7 @@ func BenchmarkUpperAppendTransactionWithMap(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -668,10 +654,7 @@ func BenchmarkUpperFind(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -688,32 +671,6 @@ func BenchmarkUpperFind(b *testing.B) { } } -// BenchmarkUpperFindWithC benchmarks upper.io/db.v2's One method. -func BenchmarkUpperFindWithC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = connectAndAddFakeRows(); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - type artistType struct { - Name string `db:"name"` - } - - var item artistType - - b.ResetTimer() - for i := 0; i < b.N; i++ { - res := sess.C("artist").Find(db.Cond{"name": artistN(i)}) - if err = res.One(&item); err != nil { - b.Fatal(err) - } - } -} - // BenchmarkUpperFindAll benchmarks upper.io/db.v2's All method. func BenchmarkUpperFindAll(b *testing.B) { var err error @@ -725,10 +682,7 @@ func BenchmarkUpperFindAll(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -810,10 +764,7 @@ func BenchmarkUpperUpdate(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -888,10 +839,7 @@ func BenchmarkUpperRemove(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -915,27 +863,7 @@ func BenchmarkUpperGetCollection(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkUpperC -func BenchmarkUpperC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - sess.C("artist") + sess.Collection("artist") } } @@ -958,10 +886,7 @@ func BenchmarkUpperCommitManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -1002,10 +927,7 @@ func BenchmarkUpperRollbackManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) diff --git a/postgresql/database.go b/postgresql/database.go index 1fd3a0d3..700daac6 100644 --- a/postgresql/database.go +++ b/postgresql/database.go @@ -28,9 +28,9 @@ import ( "database/sql" _ "github.com/lib/pq" // PostgreSQL driver. + "upper.io/db.v2" "upper.io/db.v2/builder/sqlgen" template "upper.io/db.v2/builder/template/postgresql" - "upper.io/db.v2" "upper.io/db.v2/internal/sqladapter" "upper.io/db.v2/internal/sqlutil/tx" ) @@ -67,9 +67,6 @@ func (d *database) Err(err error) error { if strings.Contains(s, `too many clients`) || strings.Contains(s, `remaining connection slots are reserved`) { return db.ErrTooManyClients } - if strings.Contains(s, `relation`) && strings.Contains(s, `does not exist`) { - return db.ErrCollectionDoesNotExist - } } return err } @@ -122,32 +119,29 @@ func (d *database) NewTable(name string) db.Collection { // Collections returns a list of non-system tables from the database. func (d *database) Collections() (collections []string, err error) { + q := d.Builder().Select("table_name"). + From("information_schema.tables"). + Where("table_schema = ?", "public") - if len(d.Schema().Tables) == 0 { - q := d.Builder().Select("table_name"). - From("information_schema.tables"). - Where("table_schema = ?", "public") - - iter := q.Iterator() - defer iter.Close() + iter := q.Iterator() + defer iter.Close() - for iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { - return nil, err - } - d.Schema().AddTable(tableName) + for iter.Next() { + var tableName string + if err := iter.Scan(&tableName); err != nil { + return nil, err } + collections = append(collections, tableName) } - return d.Schema().Tables, nil + return collections, nil } // Drop removes all tables from the current database. func (d *database) Drop() error { stmt := &sqlgen.Statement{ Type: sqlgen.DropDatabase, - Database: sqlgen.DatabaseWithName(d.Schema().Name), + Database: sqlgen.DatabaseWithName(d.Schema().Name()), } if _, err := d.Builder().Exec(stmt); err != nil { return err @@ -182,105 +176,52 @@ func (d *database) Transaction() (db.Tx, error) { // PopulateSchema looks up for the table info in the database and populates its // schema for internal use. -func (d *database) PopulateSchema() (err error) { - var collections []string - - d.NewSchema() +func (d *database) PopulateSchema() error { + schema := d.NewSchema() q := d.Builder().Select(db.Raw("CURRENT_DATABASE() AS name")) - var dbName string - iter := q.Iterator() defer iter.Close() if iter.Next() { - if err := iter.Scan(&dbName); err != nil { - return err - } - } else { - return iter.Err() - } - - d.Schema().Name = dbName - - if collections, err = d.Collections(); err != nil { + var name string + err := iter.Scan(&name) + schema.SetName(name) return err } - - for i := range collections { - if _, err = d.Collection(collections[i]); err != nil { - return err - } - } - - return err + return iter.Err() } // TableExists checks whether a table exists and returns an error in case it doesn't. func (d *database) TableExists(name string) error { - if d.Schema().HasTable(name) { - return nil - } - q := d.Builder().Select("table_name"). From("information_schema.tables"). - Where("table_catalog = ? AND table_name = ?", d.Schema().Name, name) + Where("table_catalog = ? AND table_name = ?", d.Schema().Name(), name) iter := q.Iterator() defer iter.Close() if iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { + var name string + if err := iter.Scan(&name); err != nil { return err } - } else { - return db.ErrCollectionDoesNotExist - } - - return nil -} - -// TableColumns returns all columns from the given table. -func (d *database) TableColumns(tableName string) ([]string, error) { - s := d.Schema() - - if len(s.Table(tableName).Columns) == 0 { - - q := d.Builder().Select("column_name"). - From("information_schema.columns"). - Where("table_catalog = ? AND table_name = ?", d.Schema().Name, tableName) - - var rows []struct { - Name string `db:"column_name"` - } - - if err := q.Iterator().All(&rows); err != nil { - return nil, err - } - - s.TableInfo[tableName].Columns = make([]string, 0, len(rows)) - - for i := range rows { - s.TableInfo[tableName].Columns = append(s.TableInfo[tableName].Columns, rows[i].Name) - } + return nil } - - return s.Table(tableName).Columns, nil + return db.ErrCollectionDoesNotExist } // TablePrimaryKey returns all primary keys from the given table. func (d *database) TablePrimaryKey(tableName string) ([]string, error) { - s := d.Schema() - - ts := s.Table(tableName) + tableSchema := d.Schema().Table(tableName) - if len(ts.PrimaryKey) != 0 { - return ts.PrimaryKey, nil + pk := tableSchema.PrimaryKeys() + if pk != nil { + return pk, nil } - ts.PrimaryKey = make([]string, 0, 1) + pk = []string{} q := d.Builder().Select("pg_attribute.attname AS pkey"). From("pg_index", "pg_class", "pg_attribute"). @@ -296,14 +237,16 @@ func (d *database) TablePrimaryKey(tableName string) ([]string, error) { defer iter.Close() for iter.Next() { - var pKey string - if err := iter.Scan(&pKey); err != nil { + var k string + if err := iter.Scan(&k); err != nil { return nil, err } - ts.PrimaryKey = append(ts.PrimaryKey, pKey) + pk = append(pk, k) } - return ts.PrimaryKey, nil + tableSchema.SetPrimaryKeys(pk) + + return pk, nil } func (d *database) clone() (*database, error) { diff --git a/postgresql/database_test.go b/postgresql/database_test.go index d2e8bb10..55862df4 100644 --- a/postgresql/database_test.go +++ b/postgresql/database_test.go @@ -223,31 +223,11 @@ func SkipTestOpenWithWrongData(t *testing.T) { } } -// Test Use -func TestUse(t *testing.T) { - var err error - var sess db.Database - - // Opening database, no error expected. - if sess, err = db.Open(Adapter, settings); err != nil { - t.Fatal(err) - } - - // Connecting to another database, error expected. - if err = sess.Use("Another database"); err == nil { - t.Fatal("This database should not exist!") - } - - // Closing connection. - sess.Close() -} - // Attempts to get all collections and truncate each one of them. func TestTruncate(t *testing.T) { var err error var sess db.Database var collections []string - var col db.Collection // Opening database. if sess, err = db.Open(Adapter, settings); err != nil { @@ -270,12 +250,10 @@ func TestTruncate(t *testing.T) { for _, name := range collections { // Getting a collection. - if col, err = sess.Collection(name); err != nil { - t.Fatal(err) - } + col := sess.Collection(name) // Table must exists before we can use it. - if col.Exists() == true { + if col.Exists() { // Truncating the table. if err = col.Truncate(); err != nil { t.Fatal(err) @@ -292,10 +270,7 @@ func TestSetCursorError(t *testing.T) { } defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // trigger Postgres error. "" is not an int. res := artist.Find(db.Cond{"id": ""}) @@ -322,9 +297,7 @@ func TestAppend(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Attempt to append a map. itemMap := map[string]string{ @@ -408,9 +381,7 @@ func TestResultCount(t *testing.T) { defer sess.Close() // We should close the database when it's no longer in use. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining a set with no conditions. res = artist.Find() @@ -435,10 +406,10 @@ func TestResultNonExistentCount(t *testing.T) { defer sess.Close() - total, err := sess.C("notartist").Find().Count() + total, err := sess.Collection("notartist").Find().Count() - if err != db.ErrCollectionDoesNotExist { - t.Fatal("Expecting a specific error, got", err) + if err == nil { + t.Fatal("Expecting an error") } if total != 0 { @@ -451,7 +422,6 @@ func TestResultFetch(t *testing.T) { var err error var res db.Result var sess db.Database - var artist db.Collection if sess, err = db.Open(Adapter, settings); err != nil { t.Fatal(err) @@ -459,9 +429,7 @@ func TestResultFetch(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Dumping into a map. rowMap := map[string]interface{}{} @@ -583,7 +551,6 @@ func TestResultFetch(t *testing.T) { func TestResultFetchOne(t *testing.T) { var err error var sess db.Database - var artist db.Collection if sess, err = db.Open(Adapter, settings); err != nil { t.Fatal(err) @@ -591,9 +558,7 @@ func TestResultFetchOne(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Fetching one struct var someArtist artistType @@ -627,7 +592,6 @@ func TestResultFetchOne(t *testing.T) { func TestResultFetchAll(t *testing.T) { var err error var sess db.Database - var artist db.Collection if sess, err = db.Open(Adapter, settings); err != nil { t.Fatal(err) @@ -635,9 +599,7 @@ func TestResultFetchAll(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Fetching all artists into struct artists := []artistType{} @@ -698,9 +660,7 @@ func TestInlineStructs(t *testing.T) { defer sess.Close() - if review, err = sess.Collection("review"); err != nil { - t.Fatal(err) - } + review = sess.Collection("review") if err = review.Truncate(); err != nil { t.Fatal(err) @@ -742,7 +702,6 @@ func TestInlineStructs(t *testing.T) { func TestUpdate(t *testing.T) { var err error var sess db.Database - var artist db.Collection if sess, err = db.Open(Adapter, settings); err != nil { t.Fatal(err) @@ -750,9 +709,7 @@ func TestUpdate(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := sess.Collection("artist") // Defining destination struct value := struct { @@ -859,9 +816,7 @@ func TestFunction(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") rowStruct := struct { ID uint64 @@ -957,9 +912,7 @@ func TestNullableFields(t *testing.T) { var test testType - if col, err = sess.Collection(`data_types`); err != nil { - t.Fatal(err) - } + col = sess.Collection(`data_types`) if err = col.Truncate(); err != nil { t.Fatal(err) @@ -1044,9 +997,7 @@ func TestGroup(t *testing.T) { defer sess.Close() - if stats, err = sess.Collection("stats_test"); err != nil { - t.Fatal(err) - } + stats = sess.Collection("stats_test") // Truncating table. if err = stats.Truncate(); err != nil { @@ -1092,9 +1043,7 @@ func TestRemove(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Getting the artist with id = 1 res = artist.Find(db.Cond{"id": 1}) @@ -1122,10 +1071,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { t.Fatal(err) @@ -1141,7 +1087,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // An attempt to use the same transaction must fail. - if _, err = tx.Collection("artist"); err == nil { + if err = tx.Commit(); err == nil { t.Fatalf("Illegal, transaction has already been commited.") } @@ -1152,9 +1098,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -1180,9 +1124,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") var count uint64 if count, err = artist.Find().Count(); err != nil { @@ -1200,9 +1142,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -1224,9 +1164,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -1243,9 +1181,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistType{2, "Second"}); err != nil { @@ -1272,9 +1208,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we have 4 rows. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -1298,9 +1232,7 @@ func TestCompositeKeys(t *testing.T) { defer sess.Close() - if compositeKeys, err = sess.Collection("composite_keys"); err != nil { - t.Fatal(err) - } + compositeKeys = sess.Collection("composite_keys") n := rand.Intn(100000) @@ -1353,9 +1285,7 @@ func TestDataTypes(t *testing.T) { defer sess.Close() // Getting a pointer to the "data_types" collection. - if dataTypes, err = sess.Collection("data_types"); err != nil { - t.Fatal(err) - } + dataTypes = sess.Collection("data_types") // Removing all data. if err = dataTypes.Truncate(); err != nil { @@ -1411,9 +1341,7 @@ func TestOptionTypes(t *testing.T) { defer sess.Close() - if optionTypes, err = sess.Collection("option_types"); err != nil { - t.Fatal(err) - } + optionTypes = sess.Collection("option_types") if err = optionTypes.Truncate(); err != nil { t.Fatal(err) @@ -1616,9 +1544,7 @@ func TestOptionTypeJsonbStruct(t *testing.T) { defer sess.Close() - if optionTypes, err = sess.Collection("option_types"); err != nil { - t.Fatal(err) - } + optionTypes = sess.Collection("option_types") if err = optionTypes.Truncate(); err != nil { t.Fatal(err) diff --git a/ql/benchmark_test.go b/ql/benchmark_test.go index 08072c2d..533d088c 100644 --- a/ql/benchmark_test.go +++ b/ql/benchmark_test.go @@ -339,10 +339,7 @@ func BenchmarkUpperAppend(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -369,10 +366,7 @@ func BenchmarkUpperAppendVariableArgs(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -405,10 +399,7 @@ func BenchmarkUpperAppendTransaction(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -448,10 +439,7 @@ func BenchmarkUpperAppendTransactionWithMap(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -538,10 +526,7 @@ func BenchmarkUpperFind(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -558,32 +543,6 @@ func BenchmarkUpperFind(b *testing.B) { } } -// BenchmarkUpperFindWithC benchmarks upper.io/db.v2's One method. -func BenchmarkUpperFindWithC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = connectAndAddFakeRows(); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - type artistType struct { - Name string `db:"name"` - } - - var item artistType - - b.ResetTimer() - for i := 0; i < b.N; i++ { - res := sess.C("artist").Find(db.Cond{"name": artistN(i)}) - if err = res.One(&item); err != nil { - b.Fatal(err) - } - } -} - // BenchmarkUpperFindAll benchmarks upper.io/db.v2's All method. func BenchmarkUpperFindAll(b *testing.B) { var err error @@ -595,10 +554,7 @@ func BenchmarkUpperFindAll(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -694,10 +650,7 @@ func BenchmarkUpperUpdate(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -786,10 +739,7 @@ func BenchmarkUpperRemove(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -813,27 +763,7 @@ func BenchmarkUpperGetCollection(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkUpperC -func BenchmarkUpperC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - sess.C("artist") + sess.Collection("artist") } } @@ -856,10 +786,7 @@ func BenchmarkUpperCommitManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -900,10 +827,7 @@ func BenchmarkUpperRollbackManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) diff --git a/ql/database.go b/ql/database.go index e1a3956b..91b7946a 100644 --- a/ql/database.go +++ b/ql/database.go @@ -28,9 +28,9 @@ import ( "sync/atomic" _ "github.com/cznic/ql/driver" // QL driver + "upper.io/db.v2" "upper.io/db.v2/builder/sqlgen" template "upper.io/db.v2/builder/template/ql" - "upper.io/db.v2" "upper.io/db.v2/internal/sqladapter" "upper.io/db.v2/internal/sqlutil/tx" ) @@ -156,35 +156,32 @@ func (d *database) NewTable(name string) db.Collection { // Collections returns a list of non-system tables from the database. func (d *database) Collections() (collections []string, err error) { + q := d.Builder().Select("Name"). + From("__Table") - if len(d.Schema().Tables) == 0 { - q := d.Builder().Select("Name"). - From("__Table") - - iter := q.Iterator() - defer iter.Close() + iter := q.Iterator() + defer iter.Close() - if iter.Err() != nil { - return nil, iter.Err() - } + if iter.Err() != nil { + return nil, iter.Err() + } - for iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { - return nil, err - } - d.Schema().AddTable(tableName) + for iter.Next() { + var tableName string + if err := iter.Scan(&tableName); err != nil { + return nil, err } + collections = append(collections, tableName) } - return d.Schema().Tables, nil + return collections, nil } // Drop removes all tables from the current database. func (d *database) Drop() error { stmt := &sqlgen.Statement{ Type: sqlgen.DropDatabase, - Database: sqlgen.DatabaseWithName(d.Schema().Name), + Database: sqlgen.DatabaseWithName(d.Schema().Name()), } if _, err := d.Builder().Exec(stmt); err != nil { return err @@ -220,36 +217,20 @@ func (d *database) Transaction() (db.Tx, error) { // PopulateSchema looks up for the table info in the database and populates its // schema for internal use. func (d *database) PopulateSchema() (err error) { - var collections []string - - d.NewSchema() + schema := d.NewSchema() var connURL ConnectionURL if connURL, err = ParseURL(d.ConnectionURL().String()); err != nil { return err } - d.Schema().Name = connURL.Database - - if collections, err = d.Collections(); err != nil { - return err - } + schema.SetName(connURL.Database) - for i := range collections { - if _, err = d.Collection(collections[i]); err != nil { - return err - } - } - - return err + return nil } // TableExists checks whether a table exists and returns an error in case it doesn't. func (d *database) TableExists(name string) error { - if d.Schema().HasTable(name) { - return nil - } - q := d.Builder().Select("Name"). From("__Table"). Where("Name == ?", name) @@ -258,43 +239,13 @@ func (d *database) TableExists(name string) error { defer iter.Close() if iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { + var name string + if err := iter.Scan(&name); err != nil { return err } - } else { - return db.ErrCollectionDoesNotExist - } - - return nil -} - -// TableColumns returns all columns from the given table. -func (d *database) TableColumns(tableName string) ([]string, error) { - s := d.Schema() - - if len(s.Table(tableName).Columns) == 0 { - - q := d.Builder().Select("Name"). - From("__Column"). - Where("TableName == ?", tableName) - - var rows []struct { - Name string `db:"column_name"` - } - - if err := q.Iterator().All(&rows); err != nil { - return nil, err - } - - s.TableInfo[tableName].Columns = make([]string, 0, len(rows)) - - for i := range rows { - s.TableInfo[tableName].Columns = append(s.TableInfo[tableName].Columns, rows[i].Name) - } + return nil } - - return s.Table(tableName).Columns, nil + return db.ErrCollectionDoesNotExist } // TablePrimaryKey returns all primary keys from the given table. diff --git a/ql/database_test.go b/ql/database_test.go index 43010a78..6da065f7 100644 --- a/ql/database_test.go +++ b/ql/database_test.go @@ -138,25 +138,6 @@ func TestOpenFailed(t *testing.T) { } } -// Test USE -func TestUse(t *testing.T) { - var err error - var sess db.Database - - // Opening database, no error expected. - if sess, err = db.Open(Adapter, settings); err != nil { - t.Fatal(err) - } - - // Connecting to another database, error expected. - if err = sess.Use("."); err == nil { - t.Fatal("This is not a database") - } - - // Closing connection. - sess.Close() -} - // Attempts to get all collections and truncate each one of them. func TestTruncate(t *testing.T) { var err error @@ -185,9 +166,7 @@ func TestTruncate(t *testing.T) { for _, name := range collections { // Getting a collection. - if col, err = sess.Collection(name); err != nil { - t.Fatal(err) - } + col = sess.Collection(name) // Table must exists before we can use it. if col.Exists() == true { @@ -214,9 +193,7 @@ func TestAppend(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Attempt to append a map. itemMap := map[string]string{ @@ -289,9 +266,7 @@ func TestGroup(t *testing.T) { defer sess.Close() - if stats, err = sess.Collection("stats_test"); err != nil { - t.Fatal(err) - } + stats = sess.Collection("stats_test") // Truncating table. if err = stats.Truncate(); err != nil { @@ -342,9 +317,7 @@ func TestResultCount(t *testing.T) { defer sess.Close() // We should close the database when it's no longer in use. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining a set with no conditions. res = artist.Find() @@ -369,10 +342,10 @@ func TestResultNonExistentCount(t *testing.T) { defer sess.Close() - total, err := sess.C("notartist").Find().Count() + total, err := sess.Collection("notartist").Find().Count() - if err != db.ErrCollectionDoesNotExist { - t.Fatal("Expecting a specific error, got", err) + if err == nil { + t.Fatal("Expecting error") } if total != 0 { @@ -393,9 +366,7 @@ func TestResultFetch(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Dumping into a map. rowMap := map[string]interface{}{} @@ -526,9 +497,7 @@ func TestUpdate(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining destination struct value := struct { @@ -616,9 +585,7 @@ func TestFunction(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") rowStruct := struct { ID uint64 @@ -704,9 +671,7 @@ func TestRemove(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Getting the artist with id = 1 res = artist.Find(db.Cond{"id()": 1}) @@ -738,10 +703,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { t.Fatal(err) @@ -757,7 +719,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Attempt to use the same transaction should fail. - if _, err = tx.Collection("artist"); err == nil { + if err = tx.Commit(); err == nil { t.Fatalf("Illegal, transaction has already been commited.") } @@ -768,9 +730,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{Name: "Second"}); err != nil { @@ -796,9 +756,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") var count uint64 if count, err = artist.Find().Count(); err != nil { @@ -816,9 +774,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{Name: "Second"}); err != nil { @@ -840,9 +796,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -859,9 +813,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{Name: "Second"}); err != nil { @@ -888,9 +840,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we have 4 rows. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -916,9 +866,7 @@ func TestCompositeKeys(t *testing.T) { defer sess.Close() - if compositeKeys, err = sess.Collection("composite_keys"); err != nil { - t.Fatal(err) - } + compositeKeys = sess.Collection("composite_keys") //n := rand.Intn(100000) @@ -962,62 +910,6 @@ func TestCompositeKeys(t *testing.T) { } -/* -// Attempts to add many different datatypes to a single row in a collection, -// then it tries to get the stored datatypes and check if the stored and the -// original values match. -func TestDataTypes(t *testing.T) { - var res db.Result - var sess db.Database - var dataTypes db.Collection - var err error - var id interface{} - var exists uint64 - - if sess, err = db.Open(Adapter, settings); err != nil { - t.Fatal(err) - } - - defer sess.Close() - - // Getting a pointer to the "data_types" collection. - if dataTypes, err = sess.Collection("data_types"); err != nil { - t.Fatal(err) - } - - // Removing all data. - if err = dataTypes.Truncate(); err != nil { - t.Fatal(err) - } - - // Appending our test subject. - if id, err = dataTypes.Append(testValues); err != nil { - t.Fatal(err) - } - - // Defining our set. - res = dataTypes.Find(db.Cond{"id()": id}) - - if exists, err = res.Count(); err != nil { - t.Fatal(err) - } - - if exists == 0 { - t.Fatalf("Expecting an item.") - } - - // Trying to dump the subject into an empty structure of the same type. - var item testValuesStruct - - res.One(&item) - - // The original value and the test subject must match. - if reflect.DeepEqual(item, testValues) == false { - t.Fatalf("Struct is different.") - } -} -*/ - // TestExhaustConnections simulates a "too many connections" situation // triggered by opening more transactions than available connections. // upper.io/db.v2 deals with this problem by waiting a bit more for the connection diff --git a/sqlite/benchmark_test.go b/sqlite/benchmark_test.go index 0dfa8c9d..162b9fba 100644 --- a/sqlite/benchmark_test.go +++ b/sqlite/benchmark_test.go @@ -261,10 +261,7 @@ func BenchmarkUpperAppend(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -291,10 +288,7 @@ func BenchmarkUpperAppendVariableArgs(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") artist.Truncate() @@ -327,10 +321,7 @@ func BenchmarkUpperAppendTransaction(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -370,10 +361,7 @@ func BenchmarkUpperAppendTransactionWithMap(b *testing.B) { } defer tx.Close() - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -459,10 +447,7 @@ func BenchmarkUpperFind(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -479,32 +464,6 @@ func BenchmarkUpperFind(b *testing.B) { } } -// BenchmarkUpperFindWithC benchmarks upper.io/db.v2's One method. -func BenchmarkUpperFindWithC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = connectAndAddFakeRows(); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - type artistType struct { - Name string `db:"name"` - } - - var item artistType - - b.ResetTimer() - for i := 0; i < b.N; i++ { - res := sess.C("artist").Find(db.Cond{"name": artistN(i)}) - if err = res.One(&item); err != nil { - b.Fatal(err) - } - } -} - // BenchmarkUpperFindAll benchmarks upper.io/db.v2's All method. func BenchmarkUpperFindAll(b *testing.B) { var err error @@ -516,10 +475,7 @@ func BenchmarkUpperFindAll(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -601,10 +557,7 @@ func BenchmarkUpperUpdate(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") type artistType struct { Name string `db:"name"` @@ -679,10 +632,7 @@ func BenchmarkUpperRemove(b *testing.B) { defer sess.Close() - artist, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } + artist := sess.Collection("artist") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -706,27 +656,7 @@ func BenchmarkUpperGetCollection(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := sess.Collection("artist") - if err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkUpperC -func BenchmarkUpperC(b *testing.B) { - var err error - var sess db.Database - - if sess, err = db.Open(Adapter, settings); err != nil { - b.Fatal(err) - } - - defer sess.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - sess.C("artist") + sess.Collection("artist") } } @@ -749,10 +679,7 @@ func BenchmarkUpperCommitManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) @@ -793,10 +720,7 @@ func BenchmarkUpperRollbackManyTransactions(b *testing.B) { b.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - b.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { b.Fatal(err) diff --git a/sqlite/database.go b/sqlite/database.go index c2e0ee4e..5752917f 100644 --- a/sqlite/database.go +++ b/sqlite/database.go @@ -29,10 +29,10 @@ import ( "database/sql" _ "github.com/mattn/go-sqlite3" // SQLite3 driver. + "upper.io/db.v2" "upper.io/db.v2/builder/sqlbuilder" "upper.io/db.v2/builder/sqlgen" template "upper.io/db.v2/builder/template/sqlite" - "upper.io/db.v2" "upper.io/db.v2/internal/sqladapter" "upper.io/db.v2/internal/sqlutil/tx" ) @@ -146,36 +146,29 @@ func (d *database) NewTable(name string) db.Collection { // Collections returns a list of non-system tables from the database. func (d *database) Collections() (collections []string, err error) { + q := d.Builder().Select("tbl_name"). + From("sqlite_master"). + Where("type = ?", "table") - if len(d.Schema().Tables) == 0 { - q := d.Builder().Select("tbl_name"). - From("sqlite_master"). - Where("type = ?", "table") - - iter := q.Iterator() - defer iter.Close() - - if iter.Err() != nil { - return nil, iter.Err() - } + iter := q.Iterator() + defer iter.Close() - for iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { - return nil, err - } - d.Schema().AddTable(tableName) + for iter.Next() { + var tableName string + if err := iter.Scan(&tableName); err != nil { + return nil, err } + collections = append(collections, tableName) } - return d.Schema().Tables, nil + return collections, nil } // Drop removes all tables from the current database. func (d *database) Drop() error { stmt := &sqlgen.Statement{ Type: sqlgen.DropDatabase, - Database: sqlgen.DatabaseWithName(d.Schema().Name), + Database: sqlgen.DatabaseWithName(d.Schema().Name()), } if _, err := d.Builder().Exec(stmt); err != nil { return err @@ -211,36 +204,20 @@ func (d *database) Transaction() (db.Tx, error) { // PopulateSchema looks up for the table info in the database and populates its // schema for internal use. func (d *database) PopulateSchema() (err error) { - var collections []string - - d.NewSchema() + schema := d.NewSchema() var connURL ConnectionURL if connURL, err = ParseURL(d.ConnectionURL().String()); err != nil { return err } - d.Schema().Name = connURL.Database + schema.SetName(connURL.Database) - if collections, err = d.Collections(); err != nil { - return err - } - - for i := range collections { - if _, err = d.Collection(collections[i]); err != nil { - return err - } - } - - return err + return nil } // TableExists checks whether a table exists and returns an error in case it doesn't. func (d *database) TableExists(name string) error { - if d.Schema().HasTable(name) { - return nil - } - q := d.Builder().Select("tbl_name"). From("sqlite_master"). Where("type = 'table' AND tbl_name = ?", name) @@ -249,77 +226,62 @@ func (d *database) TableExists(name string) error { defer iter.Close() if iter.Next() { - var tableName string - if err := iter.Scan(&tableName); err != nil { + var name string + if err := iter.Scan(&name); err != nil { return err } - } else { - return db.ErrCollectionDoesNotExist + return nil } - - return nil + return db.ErrCollectionDoesNotExist } -// TableColumns returns all columns from the given table. -func (d *database) TableColumns(tableName string) ([]string, error) { - s := d.Schema() - - if len(s.Table(tableName).Columns) == 0 { - - stmt := sqlgen.RawSQL(fmt.Sprintf(`PRAGMA TABLE_INFO('%s')`, tableName)) - - rows, err := d.Builder().Query(stmt) - if err != nil { - return nil, err - } - - if d.columns == nil { - d.columns = make(map[string][]columnSchemaT) - } - - columns := []columnSchemaT{} +// TablePrimaryKey returns all primary keys from the given table. +func (d *database) TablePrimaryKey(tableName string) ([]string, error) { + tableSchema := d.Schema().Table(tableName) - if err := sqlbuilder.NewIterator(rows).All(&columns); err != nil { - return nil, err - } + pk := tableSchema.PrimaryKeys() + if pk != nil { + return pk, nil + } - d.columns[tableName] = columns + pk = []string{} - s.TableInfo[tableName].Columns = make([]string, 0, len(columns)) + stmt := sqlgen.RawSQL(fmt.Sprintf(`PRAGMA TABLE_INFO('%s')`, tableName)) - for _, col := range d.columns[tableName] { - s.TableInfo[tableName].Columns = append(s.TableInfo[tableName].Columns, col.Name) - } + rows, err := d.Builder().Query(stmt) + if err != nil { + return nil, err } - return s.Table(tableName).Columns, nil -} + if d.columns == nil { + d.columns = make(map[string][]columnSchemaT) + } -// TablePrimaryKey returns all primary keys from the given table. -func (d *database) TablePrimaryKey(tableName string) ([]string, error) { - tableSchema := d.Schema().Table(tableName) + columns := []columnSchemaT{} - d.TableColumns(tableName) + if err := sqlbuilder.NewIterator(rows).All(&columns); err != nil { + return nil, err + } maxValue := -1 - for i := range d.columns[tableName] { - if d.columns[tableName][i].PK > 0 && d.columns[tableName][i].PK > maxValue { - maxValue = d.columns[tableName][i].PK + for _, column := range columns { + if column.PK > 0 && column.PK > maxValue { + maxValue = column.PK } } if maxValue > 0 { - tableSchema.PrimaryKey = make([]string, maxValue) - - for i := range d.columns[tableName] { - if d.columns[tableName][i].PK > 0 { - tableSchema.PrimaryKey[d.columns[tableName][i].PK-1] = d.columns[tableName][i].Name + for _, column := range columns { + if column.PK > 0 { + pk = append(pk, column.Name) } } } - return tableSchema.PrimaryKey, nil + tableSchema.SetPrimaryKeys(pk) + + return pk, nil } func (d *database) clone() (*database, error) { diff --git a/sqlite/database_test.go b/sqlite/database_test.go index 6f68da14..38a8f3ec 100644 --- a/sqlite/database_test.go +++ b/sqlite/database_test.go @@ -217,9 +217,7 @@ func TestTruncate(t *testing.T) { for _, name := range collections { // Getting a collection. - if col, err = sess.Collection(name); err != nil { - t.Fatal(err) - } + col = sess.Collection(name) // Table must exists before we can use it. if col.Exists() == true { @@ -245,9 +243,7 @@ func TestAppend(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Attempt to append a map. itemMap := map[string]string{ @@ -338,9 +334,7 @@ func TestNullableFields(t *testing.T) { var test testType - if col, err = sess.Collection(`data_types`); err != nil { - t.Fatal(err) - } + col = sess.Collection(`data_types`) if err = col.Truncate(); err != nil { t.Fatal(err) @@ -426,9 +420,7 @@ func TestGroup(t *testing.T) { defer sess.Close() - if stats, err = sess.Collection("stats_test"); err != nil { - t.Fatal(err) - } + stats = sess.Collection("stats_test") // Truncating table. if err = stats.Truncate(); err != nil { @@ -479,9 +471,7 @@ func TestResultCount(t *testing.T) { defer sess.Close() // We should close the database when it's no longer in use. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining a set with no conditions. res = artist.Find() @@ -506,10 +496,10 @@ func TestResultNonExistentCount(t *testing.T) { defer sess.Close() - total, err := sess.C("notartist").Find().Count() + total, err := sess.Collection("notartist").Find().Count() - if err != db.ErrCollectionDoesNotExist { - t.Fatal("Expecting a specific error, got", err) + if err == nil { + t.Fatal("Expecting an error") } if total != 0 { @@ -530,9 +520,7 @@ func TestResultFetch(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Dumping into a map. rowMap := map[string]interface{}{} @@ -663,9 +651,7 @@ func TestUpdate(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Defining destination struct value := struct { @@ -753,9 +739,7 @@ func TestFunction(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") rowStruct := struct { ID uint64 @@ -841,9 +825,7 @@ func TestRemove(t *testing.T) { defer sess.Close() - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") // Getting the artist with id = 1 res = artist.Find(db.Cond{"id": 1}) @@ -876,10 +858,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - var artist db.Collection - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist := tx.Collection("artist") if err = artist.Truncate(); err != nil { t.Fatal(err) @@ -895,7 +874,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Attempt to use the same transaction should fail. - if _, err = tx.Collection("artist"); err == nil { + if err = tx.Commit(); err == nil { t.Fatalf("Illegal, transaction has already been commited.") } @@ -906,9 +885,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{2, "Second"}); err != nil { @@ -934,9 +911,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") var count uint64 if count, err = artist.Find().Count(); err != nil { @@ -954,9 +929,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{2, "Second"}); err != nil { @@ -978,9 +951,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we still have one element. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -997,9 +968,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if artist, err = tx.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = tx.Collection("artist") // Won't fail. if _, err = artist.Append(artistT{2, "Second"}); err != nil { @@ -1026,9 +995,7 @@ func TestTransactionsAndRollback(t *testing.T) { } // Let's verify we have 4 rows. - if artist, err = sess.Collection("artist"); err != nil { - t.Fatal(err) - } + artist = sess.Collection("artist") if count, err = artist.Find().Count(); err != nil { t.Fatal(err) @@ -1052,9 +1019,7 @@ func TestCompositeKeys(t *testing.T) { defer sess.Close() - if compositeKeys, err = sess.Collection("composite_keys"); err != nil { - t.Fatal(err) - } + compositeKeys = sess.Collection("composite_keys") n := rand.Intn(100000) @@ -1106,9 +1071,7 @@ func TestDataTypes(t *testing.T) { defer sess.Close() // Getting a pointer to the "data_types" collection. - if dataTypes, err = sess.Collection("data_types"); err != nil { - t.Fatal(err) - } + dataTypes = sess.Collection("data_types") // Removing all data. if err = dataTypes.Truncate(); err != nil { -- GitLab