From db00617c192d3e2e9acb56d4673d038f9d466e94 Mon Sep 17 00:00:00 2001 From: bitxel Date: Thu, 14 Jun 2018 00:08:05 +0800 Subject: [PATCH 1/3] Override view name in querier --- internal/test/sql/mssql_init.sql | 2 ++ internal/test/sql/mysql_init.sql | 2 ++ internal/test/sql/postgres_init.sql | 2 ++ internal/test/sql/sqlite3_init.sql | 2 ++ querier.go | 15 +++++++++++++-- querier_examples_test.go | 11 +++++++++++ querier_selects.go | 10 +++++----- reform-db/cmd_init_test.go | 6 +++++- 8 files changed, 42 insertions(+), 8 deletions(-) diff --git a/internal/test/sql/mssql_init.sql b/internal/test/sql/mssql_init.sql index 881e8e1b..699235b5 100644 --- a/internal/test/sql/mssql_init.sql +++ b/internal/test/sql/mssql_init.sql @@ -7,6 +7,8 @@ CREATE TABLE [people] ( [updated_at] datetime2 ); +EXEC('CREATE VIEW [people_copy] AS SELECT * from [people]') + CREATE TABLE [projects] ( [name] varchar(255) NOT NULL, [id] varchar(255) PRIMARY KEY, diff --git a/internal/test/sql/mysql_init.sql b/internal/test/sql/mysql_init.sql index 7f38c77b..3cd72d2c 100644 --- a/internal/test/sql/mysql_init.sql +++ b/internal/test/sql/mysql_init.sql @@ -11,6 +11,8 @@ CREATE TABLE people ( PRIMARY KEY (id) ); +CREATE VIEW people_copy AS SELECT * from people; + CREATE TABLE projects ( name varchar(255) NOT NULL, id varchar(255) NOT NULL, diff --git a/internal/test/sql/postgres_init.sql b/internal/test/sql/postgres_init.sql index 8f497101..a24a0865 100644 --- a/internal/test/sql/postgres_init.sql +++ b/internal/test/sql/postgres_init.sql @@ -9,6 +9,8 @@ CREATE TABLE people ( -- updated_at timestamp without time zone ); +CREATE VIEW people_copy AS SELECT * from people; + CREATE TABLE projects ( name varchar NOT NULL, id varchar PRIMARY KEY, diff --git a/internal/test/sql/sqlite3_init.sql b/internal/test/sql/sqlite3_init.sql index 2b969b09..3ece9a2f 100644 --- a/internal/test/sql/sqlite3_init.sql +++ b/internal/test/sql/sqlite3_init.sql @@ -7,6 +7,8 @@ CREATE TABLE people ( updated_at datetime ); +CREATE VIEW people_copy AS SELECT * from people; + CREATE TABLE projects ( name varchar NOT NULL, id varchar NOT NULL PRIMARY KEY, diff --git a/querier.go b/querier.go index 43fff68e..9fd1baf7 100644 --- a/querier.go +++ b/querier.go @@ -8,8 +8,9 @@ import ( // Querier performs queries and commands. type Querier struct { - dbtx DBTX - tag string + dbtx DBTX + tag string + viewName string Dialect Logger Logger } @@ -53,9 +54,19 @@ func (q *Querier) WithTag(format string, args ...interface{}) *Querier { return newQ } +// WithView returns a copy of Querier with appointed view name. +func (q *Querier) WithView(viewName string) *Querier { + newQ := newQuerier(q.dbtx, q.Dialect, q.Logger) + newQ.viewName = viewName + return newQ +} + // QualifiedView returns quoted qualified view name. func (q *Querier) QualifiedView(view View) string { v := q.QuoteIdentifier(view.Name()) + if q.viewName != "" { + v = q.QuoteIdentifier(q.viewName) + } if view.Schema() != "" { v = q.QuoteIdentifier(view.Schema()) + "." + v } diff --git a/querier_examples_test.go b/querier_examples_test.go index d99660ab..323feadc 100644 --- a/querier_examples_test.go +++ b/querier_examples_test.go @@ -81,6 +81,17 @@ func ExampleQuerier_WithTag() { // Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time) } +func ExampleQuerier_WithView() { + id := 1 + person, err := DB.WithView("people_copy").FindByPrimaryKeyFrom(PersonTable, id) + if err != nil { + log.Fatal(err) + } + fmt.Println(person) + // Output: + // ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: (*time.Time) +} + func ExampleQuerier_SelectRows() { tail := fmt.Sprintf("WHERE created_at < %s ORDER BY id", DB.Placeholder(1)) y2010 := time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC) diff --git a/querier_selects.go b/querier_selects.go index 63d94fc5..464a81b6 100644 --- a/querier_selects.go +++ b/querier_selects.go @@ -120,8 +120,8 @@ func (q *Querier) SelectAllFrom(view View, tail string, args ...interface{}) (st } // findTail returns a tail of SELECT query for given view, column and arg. -func (q *Querier) findTail(view string, column string, arg interface{}, limit1 bool) (tail string, needArg bool) { - qi := q.QuoteIdentifier(view) + "." + q.QuoteIdentifier(column) +func (q *Querier) findTail(view View, column string, arg interface{}, limit1 bool) (tail string, needArg bool) { + qi := q.QualifiedView(view) + "." + q.QuoteIdentifier(column) if arg == nil { tail = fmt.Sprintf("WHERE %s IS NULL", qi) } else { @@ -142,7 +142,7 @@ func (q *Querier) findTail(view string, column string, arg interface{}, limit1 b // If there are no rows in result, it returns ErrNoRows. It also may return QueryRow(), Scan() // and AfterFinder errors. func (q *Querier) FindOneTo(str Struct, column string, arg interface{}) error { - tail, needArg := q.findTail(str.View().Name(), column, arg, true) + tail, needArg := q.findTail(str.View(), column, arg, true) if needArg { return q.SelectOneTo(str, tail, arg) } @@ -155,7 +155,7 @@ func (q *Querier) FindOneTo(str Struct, column string, arg interface{}) error { // If there are no rows in result, it returns nil, ErrNoRows. It also may return QueryRow(), Scan() // and AfterFinder errors. func (q *Querier) FindOneFrom(view View, column string, arg interface{}) (Struct, error) { - tail, needArg := q.findTail(view.Name(), column, arg, true) + tail, needArg := q.findTail(view, column, arg, true) if needArg { return q.SelectOneFrom(view, tail, arg) } @@ -169,7 +169,7 @@ func (q *Querier) FindOneFrom(view View, column string, arg interface{}) (Struct // // See SelectRows example for idiomatic usage. func (q *Querier) FindRows(view View, column string, arg interface{}) (*sql.Rows, error) { - tail, needArg := q.findTail(view.Name(), column, arg, false) + tail, needArg := q.findTail(view, column, arg, false) if needArg { return q.SelectRows(view, tail, arg) } diff --git a/reform-db/cmd_init_test.go b/reform-db/cmd_init_test.go index d9afe0f7..2fb776d4 100644 --- a/reform-db/cmd_init_test.go +++ b/reform-db/cmd_init_test.go @@ -38,7 +38,11 @@ func (s *ReformDBSuite) TestInit() { fis, err := ioutil.ReadDir(dir) s.Require().NoError(err) - s.Require().Len(fis, 4) + if s.db.Dialect == sqlite3.Dialect { + s.Require().Len(fis, 4) // generate 4 struct files for sqlite3 + } else { + s.Require().Len(fis, 5) // generate 5 struct files for other DBs + } ff := filepath.Join(dir, "people.go") actual, err := parse.File(ff) From d19406385734728adc8180337fb0f3077d09ef16 Mon Sep 17 00:00:00 2001 From: Alexey Palazhchenko Date: Tue, 7 Aug 2018 07:45:43 +0300 Subject: [PATCH 2/3] Add better example. --- internal/test/sql/mssql_init.sql | 5 ++++- internal/test/sql/mysql_init.sql | 4 +++- internal/test/sql/postgres_init.sql | 4 +++- internal/test/sql/sqlite3_init.sql | 4 +++- querier_examples_test.go | 7 ++++--- reform-db/cmd_init_test.go | 6 +----- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/internal/test/sql/mssql_init.sql b/internal/test/sql/mssql_init.sql index 699235b5..40ba5501 100644 --- a/internal/test/sql/mssql_init.sql +++ b/internal/test/sql/mssql_init.sql @@ -7,7 +7,10 @@ CREATE TABLE [people] ( [updated_at] datetime2 ); -EXEC('CREATE VIEW [people_copy] AS SELECT * from [people]') +-- EXEC is workaround for "'CREATE VIEW' must be the first statement in a query batch." +EXEC('CREATE VIEW [people_0] AS SELECT * FROM [people] WHERE (id % 3) = 0'); +EXEC('CREATE VIEW [people_1] AS SELECT * FROM [people] WHERE (id % 3) = 1'); +EXEC('CREATE VIEW [people_2] AS SELECT * FROM [people] WHERE (id % 3) = 2'); CREATE TABLE [projects] ( [name] varchar(255) NOT NULL, diff --git a/internal/test/sql/mysql_init.sql b/internal/test/sql/mysql_init.sql index 3cd72d2c..e6383ac4 100644 --- a/internal/test/sql/mysql_init.sql +++ b/internal/test/sql/mysql_init.sql @@ -11,7 +11,9 @@ CREATE TABLE people ( PRIMARY KEY (id) ); -CREATE VIEW people_copy AS SELECT * from people; +CREATE VIEW people_0 AS SELECT * FROM people WHERE (id % 3) = 0; +CREATE VIEW people_1 AS SELECT * FROM people WHERE (id % 3) = 1; +CREATE VIEW people_2 AS SELECT * FROM people WHERE (id % 3) = 2; CREATE TABLE projects ( name varchar(255) NOT NULL, diff --git a/internal/test/sql/postgres_init.sql b/internal/test/sql/postgres_init.sql index a24a0865..4dd2f095 100644 --- a/internal/test/sql/postgres_init.sql +++ b/internal/test/sql/postgres_init.sql @@ -9,7 +9,9 @@ CREATE TABLE people ( -- updated_at timestamp without time zone ); -CREATE VIEW people_copy AS SELECT * from people; +CREATE VIEW people_0 AS SELECT * FROM people WHERE (id % 3) = 0; +CREATE VIEW people_1 AS SELECT * FROM people WHERE (id % 3) = 1; +CREATE VIEW people_2 AS SELECT * FROM people WHERE (id % 3) = 2; CREATE TABLE projects ( name varchar NOT NULL, diff --git a/internal/test/sql/sqlite3_init.sql b/internal/test/sql/sqlite3_init.sql index 3ece9a2f..7777ad01 100644 --- a/internal/test/sql/sqlite3_init.sql +++ b/internal/test/sql/sqlite3_init.sql @@ -7,7 +7,9 @@ CREATE TABLE people ( updated_at datetime ); -CREATE VIEW people_copy AS SELECT * from people; +CREATE VIEW people_0 AS SELECT * FROM people WHERE (id % 3) = 0; +CREATE VIEW people_1 AS SELECT * FROM people WHERE (id % 3) = 1; +CREATE VIEW people_2 AS SELECT * FROM people WHERE (id % 3) = 2; CREATE TABLE projects ( name varchar NOT NULL, diff --git a/querier_examples_test.go b/querier_examples_test.go index 323feadc..964c51a4 100644 --- a/querier_examples_test.go +++ b/querier_examples_test.go @@ -83,13 +83,14 @@ func ExampleQuerier_WithTag() { func ExampleQuerier_WithView() { id := 1 - person, err := DB.WithView("people_copy").FindByPrimaryKeyFrom(PersonTable, id) + view := fmt.Sprintf("people_%d", id%3) + person, err := DB.WithView(view).FindByPrimaryKeyFrom(PersonTable, id) if err != nil { log.Fatal(err) } - fmt.Println(person) + fmt.Printf("%s: %s", view, person) // Output: - // ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: (*time.Time) + // people_1: ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: (*time.Time) } func ExampleQuerier_SelectRows() { diff --git a/reform-db/cmd_init_test.go b/reform-db/cmd_init_test.go index 2fb776d4..9d39e116 100644 --- a/reform-db/cmd_init_test.go +++ b/reform-db/cmd_init_test.go @@ -38,11 +38,7 @@ func (s *ReformDBSuite) TestInit() { fis, err := ioutil.ReadDir(dir) s.Require().NoError(err) - if s.db.Dialect == sqlite3.Dialect { - s.Require().Len(fis, 4) // generate 4 struct files for sqlite3 - } else { - s.Require().Len(fis, 5) // generate 5 struct files for other DBs - } + s.Require().Len(fis, 7) // 4 tables + views people_0, people_1, people_2 ff := filepath.Join(dir, "people.go") actual, err := parse.File(ff) From 27faa85258f7452930b8f1fff4f478dca3dbf9dd Mon Sep 17 00:00:00 2001 From: Alexey Palazhchenko Date: Tue, 7 Aug 2018 09:08:20 +0300 Subject: [PATCH 3/3] Fix overriding of view name in Querier. --- querier.go | 25 +++++++++++++------------ querier_examples_test.go | 14 ++++++++------ querier_selects.go | 8 ++++++-- querier_test.go | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 querier_test.go diff --git a/querier.go b/querier.go index 9fd1baf7..c3803504 100644 --- a/querier.go +++ b/querier.go @@ -8,9 +8,9 @@ import ( // Querier performs queries and commands. type Querier struct { - dbtx DBTX - tag string - viewName string + dbtx DBTX + tag string + qualifiedViewName string Dialect Logger Logger } @@ -51,24 +51,25 @@ func (q *Querier) WithTag(format string, args ...interface{}) *Querier { } else { newQ.tag = fmt.Sprintf(format, args...) } + newQ.qualifiedViewName = q.qualifiedViewName return newQ } -// WithView returns a copy of Querier with appointed view name. -func (q *Querier) WithView(viewName string) *Querier { +// WithQualifiedViewName returns a copy of Querier with set qualified view name. +// Returned Querier is tied to the same DB or TX. +// TODO Support INSERT/UPDATE/DELETE. More test. +func (q *Querier) WithQualifiedViewName(qualifiedViewName string) *Querier { newQ := newQuerier(q.dbtx, q.Dialect, q.Logger) - newQ.viewName = viewName + newQ.tag = q.tag + newQ.qualifiedViewName = qualifiedViewName return newQ } -// QualifiedView returns quoted qualified view name. +// QualifiedView returns quoted qualified view name of given view. func (q *Querier) QualifiedView(view View) string { v := q.QuoteIdentifier(view.Name()) - if q.viewName != "" { - v = q.QuoteIdentifier(q.viewName) - } - if view.Schema() != "" { - v = q.QuoteIdentifier(view.Schema()) + "." + v + if s := view.Schema(); s != "" { + v = q.QuoteIdentifier(s) + "." + v } return v } diff --git a/querier_examples_test.go b/querier_examples_test.go index 964c51a4..5a28ffe4 100644 --- a/querier_examples_test.go +++ b/querier_examples_test.go @@ -81,16 +81,18 @@ func ExampleQuerier_WithTag() { // Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time) } -func ExampleQuerier_WithView() { - id := 1 - view := fmt.Sprintf("people_%d", id%3) - person, err := DB.WithView(view).FindByPrimaryKeyFrom(PersonTable, id) +func ExampleQuerier_WithQualifiedViewName() { + _, err := DB.WithQualifiedViewName("people_0").FindByPrimaryKeyFrom(PersonTable, 1) + if err != reform.ErrNoRows { + log.Fatal(err) + } + person, err := DB.WithQualifiedViewName("people_1").FindByPrimaryKeyFrom(PersonTable, 1) if err != nil { log.Fatal(err) } - fmt.Printf("%s: %s", view, person) + fmt.Println(person) // Output: - // people_1: ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: (*time.Time) + // ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: (*time.Time) } func ExampleQuerier_SelectRows() { diff --git a/querier_selects.go b/querier_selects.go index 464a81b6..e1557c4a 100644 --- a/querier_selects.go +++ b/querier_selects.go @@ -42,8 +42,12 @@ func (q *Querier) selectQuery(view View, tail string, limit1 bool) string { query += " TOP 1" } - return fmt.Sprintf("%s %s FROM %s %s", - query, strings.Join(q.QualifiedColumns(view), ", "), q.QualifiedView(view), tail) + from := q.QualifiedView(view) + if q.qualifiedViewName != "" { + from = q.qualifiedViewName + " AS " + from + } + + return fmt.Sprintf("%s %s FROM %s %s", query, strings.Join(q.QualifiedColumns(view), ", "), from, tail) } // SelectOneTo queries str's View with tail and args and scans first result to str. diff --git a/querier_test.go b/querier_test.go new file mode 100644 index 00000000..c7de162f --- /dev/null +++ b/querier_test.go @@ -0,0 +1,35 @@ +package reform_test + +import ( + "gopkg.in/reform.v1/dialects/mssql" + "gopkg.in/reform.v1/dialects/mysql" + "gopkg.in/reform.v1/dialects/postgresql" + "gopkg.in/reform.v1/dialects/sqlite3" + "gopkg.in/reform.v1/dialects/sqlserver" + . "gopkg.in/reform.v1/internal/test/models" +) + +func (s *ReformSuite) TestQualifiedView() { + switch s.q.Dialect { + case postgresql.Dialect: + s.Equal(`"people"`, s.q.QualifiedView(PersonTable)) + s.Equal(`"people"`, s.q.WithQualifiedViewName("ignored").QualifiedView(PersonTable)) + s.Equal(`"legacy"."people"`, s.q.QualifiedView(LegacyPersonTable)) + s.Equal(`"legacy"."people"`, s.q.WithQualifiedViewName("ignored").QualifiedView(LegacyPersonTable)) + + case mysql.Dialect: + s.Equal("`people`", s.q.QualifiedView(PersonTable)) + s.Equal("`people`", s.q.WithQualifiedViewName("ignored").QualifiedView(PersonTable)) + + case sqlite3.Dialect: + s.Equal(`"people"`, s.q.QualifiedView(PersonTable)) + s.Equal(`"people"`, s.q.WithQualifiedViewName("ignored").QualifiedView(PersonTable)) + + case mssql.Dialect, sqlserver.Dialect: + s.Equal(`[people]`, s.q.QualifiedView(PersonTable)) + s.Equal(`[people]`, s.q.WithQualifiedViewName("ignored").QualifiedView(PersonTable)) + + default: + s.Fail("Unhandled dialect", s.q.Dialect.String()) + } +}