From b36f699b36852250e71449966ea1be06d7e6d5f8 Mon Sep 17 00:00:00 2001 From: Muhammad Surya Date: Thu, 1 Apr 2021 21:15:05 +0900 Subject: [PATCH 1/4] Remove timestamp and bump rel --- go.mod | 2 +- go.sum | 2 ++ mssql.go | 2 -- mssql_test.go | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 82d9b1a..9514469 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/denisenkom/go-mssqldb v0.9.0 - github.com/go-rel/rel v0.12.0 + github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9 github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index 04a1e5b..3799b79 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-rel/rel v0.12.0 h1:C3r8YSwjqcXFtdANY8eB9nkMD0cfwThqnQdesmVt5s8= github.com/go-rel/rel v0.12.0/go.mod h1:XgnuQgSs6iSOQaZ+fp+uz4GdYsdyKcnAKkc0WhkXVJI= +github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9 h1:24uAN3d6g7Df23D6rUi/Bm5brD69vCbvGjqKoPuXxbE= +github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9/go.mod h1:XgnuQgSs6iSOQaZ+fp+uz4GdYsdyKcnAKkc0WhkXVJI= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= diff --git a/mssql.go b/mssql.go index 5e565ff..ce8ddc4 100644 --- a/mssql.go +++ b/mssql.go @@ -151,8 +151,6 @@ func mapColumn(column *rel.Column) (string, int, int) { case rel.Time: typ = "TIME" timeLayout = "15:04:05" - case rel.Timestamp: - typ = "TIMESTAMP" default: typ = string(column.Type) } diff --git a/mssql_test.go b/mssql_test.go index 3f120f3..377ae72 100644 --- a/mssql_test.go +++ b/mssql_test.go @@ -38,6 +38,7 @@ func TestAdapter_specs(t *testing.T) { // Query Specs specs.Query(t, repo) specs.QueryJoin(t, repo) + specs.QueryWhereSubQuery(t, repo) specs.QueryNotFound(t, repo) // Preload specs From 7c1c9e30cc6f8e369d91165debe4fe478ee0dc38 Mon Sep 17 00:00:00 2001 From: Muhammad Surya Date: Thu, 1 Apr 2021 21:18:45 +0900 Subject: [PATCH 2/4] Separate sub query test --- mssql_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/mssql_test.go b/mssql_test.go index 377ae72..3f120f3 100644 --- a/mssql_test.go +++ b/mssql_test.go @@ -38,7 +38,6 @@ func TestAdapter_specs(t *testing.T) { // Query Specs specs.Query(t, repo) specs.QueryJoin(t, repo) - specs.QueryWhereSubQuery(t, repo) specs.QueryNotFound(t, repo) // Preload specs From 245fd06751e5c086890910d84faa17fc3b5db727 Mon Sep 17 00:00:00 2001 From: Muhammad Surya Date: Sat, 3 Apr 2021 16:18:36 +0900 Subject: [PATCH 3/4] bump rel --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 9514469..29b48fb 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/denisenkom/go-mssqldb v0.9.0 - github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9 + github.com/go-rel/rel v0.12.1-0.20210401230812-23ab585f43ad github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index 3799b79..a8dc4d5 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,8 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-rel/rel v0.12.0 h1:C3r8YSwjqcXFtdANY8eB9nkMD0cfwThqnQdesmVt5s8= -github.com/go-rel/rel v0.12.0/go.mod h1:XgnuQgSs6iSOQaZ+fp+uz4GdYsdyKcnAKkc0WhkXVJI= -github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9 h1:24uAN3d6g7Df23D6rUi/Bm5brD69vCbvGjqKoPuXxbE= -github.com/go-rel/rel v0.12.1-0.20210401102123-e153542bb2f9/go.mod h1:XgnuQgSs6iSOQaZ+fp+uz4GdYsdyKcnAKkc0WhkXVJI= +github.com/go-rel/rel v0.12.1-0.20210401230812-23ab585f43ad h1:GWwTNkaLp5g1284FgZxf7CvktOjLIsx/l79F3WZbgoc= +github.com/go-rel/rel v0.12.1-0.20210401230812-23ab585f43ad/go.mod h1:XgnuQgSs6iSOQaZ+fp+uz4GdYsdyKcnAKkc0WhkXVJI= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= From 0054b946f41d1a44526a02e8e7cb94e753b3ad4a Mon Sep 17 00:00:00 2001 From: Muhammad Surya Date: Sat, 3 Apr 2021 16:47:29 +0900 Subject: [PATCH 4/4] Implement subquery --- delete_sql.go | 6 ++++-- filter_sql.go | 55 +++++++++++++++++++++++++++++++++++++++------------ mssql.go | 4 ++-- mssql_test.go | 1 + query_sql.go | 26 ++++++++++++++---------- update_sql.go | 6 ++++-- 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/delete_sql.go b/delete_sql.go index cc41ce6..5823b2a 100644 --- a/delete_sql.go +++ b/delete_sql.go @@ -7,6 +7,7 @@ import ( // DeleteSQL builder. type DeleteSQL struct { fieldSQL FieldSQL + querySQL QuerySQL filterSQL FilterSQL } @@ -19,7 +20,7 @@ func (ds DeleteSQL) Build(table string, filter rel.FilterQuery) (string, []inter if !filter.None() { buffer.WriteString(" WHERE ") - ds.filterSQL.Write(&buffer, filter) + ds.filterSQL.Write(&buffer, filter, ds.querySQL) } buffer.WriteString(";") @@ -28,9 +29,10 @@ func (ds DeleteSQL) Build(table string, filter rel.FilterQuery) (string, []inter } // NewDeleteSQL builder. -func NewDeleteSQL(fieldSQL FieldSQL, filterSQL FilterSQL) DeleteSQL { +func NewDeleteSQL(fieldSQL FieldSQL, querySQL QuerySQL, filterSQL FilterSQL) DeleteSQL { return DeleteSQL{ fieldSQL: fieldSQL, + querySQL: querySQL, filterSQL: filterSQL, } } diff --git a/filter_sql.go b/filter_sql.go index 4f9e361..5623e28 100644 --- a/filter_sql.go +++ b/filter_sql.go @@ -10,22 +10,22 @@ type FilterSQL struct { } // Write SQL to buffer. -func (fs FilterSQL) Write(buffer *buffer, filter rel.FilterQuery) { +func (fs FilterSQL) Write(buffer *buffer, filter rel.FilterQuery, qs QuerySQL) { switch filter.Type { case rel.FilterAndOp: - fs.BuildLogical(buffer, "AND", filter.Inner) + fs.BuildLogical(buffer, "AND", filter.Inner, qs) case rel.FilterOrOp: - fs.BuildLogical(buffer, "OR", filter.Inner) + fs.BuildLogical(buffer, "OR", filter.Inner, qs) case rel.FilterNotOp: buffer.WriteString("NOT ") - fs.BuildLogical(buffer, "AND", filter.Inner) + fs.BuildLogical(buffer, "AND", filter.Inner, qs) case rel.FilterEqOp, rel.FilterNeOp, rel.FilterLtOp, rel.FilterLteOp, rel.FilterGtOp, rel.FilterGteOp: - fs.BuildComparison(buffer, filter) + fs.BuildComparison(buffer, filter, qs) case rel.FilterNilOp: buffer.WriteString(fs.fieldSQL.Build(filter.Field)) buffer.WriteString(" IS NULL") @@ -34,7 +34,7 @@ func (fs FilterSQL) Write(buffer *buffer, filter rel.FilterQuery) { buffer.WriteString(" IS NOT NULL") case rel.FilterInOp, rel.FilterNinOp: - fs.BuildInclusion(buffer, filter) + fs.BuildInclusion(buffer, filter, qs) case rel.FilterLikeOp: buffer.WriteString(fs.fieldSQL.Build(filter.Field)) buffer.WriteString(" LIKE ") @@ -50,7 +50,7 @@ func (fs FilterSQL) Write(buffer *buffer, filter rel.FilterQuery) { } // BuildLogical SQL to buffer. -func (fs FilterSQL) BuildLogical(buffer *buffer, op string, inner []rel.FilterQuery) { +func (fs FilterSQL) BuildLogical(buffer *buffer, op string, inner []rel.FilterQuery, qs QuerySQL) { var ( length = len(inner) ) @@ -60,7 +60,7 @@ func (fs FilterSQL) BuildLogical(buffer *buffer, op string, inner []rel.FilterQu } for i, c := range inner { - fs.Write(buffer, c) + fs.Write(buffer, c, qs) if i < length-1 { buffer.WriteByte(' ') @@ -75,7 +75,7 @@ func (fs FilterSQL) BuildLogical(buffer *buffer, op string, inner []rel.FilterQu } // BuildComparison SQL to buffer. -func (fs FilterSQL) BuildComparison(buffer *buffer, filter rel.FilterQuery) { +func (fs FilterSQL) BuildComparison(buffer *buffer, filter rel.FilterQuery, qs QuerySQL) { buffer.WriteString(fs.fieldSQL.Build(filter.Field)) switch filter.Type { @@ -93,11 +93,21 @@ func (fs FilterSQL) BuildComparison(buffer *buffer, filter rel.FilterQuery) { buffer.WriteString(">=") } - buffer.WriteValue(filter.Value) + switch v := filter.Value.(type) { + case rel.SubQuery: + // For warped sub-queries + fs.buildSubQuery(buffer, v, qs) + case rel.Query: + // For sub-queries without warp + fs.buildSubQuery(buffer, rel.SubQuery{Query: v}, qs) + default: + // For simple values + buffer.WriteValue(filter.Value) + } } // BuildInclusion SQL to buffer. -func (fs FilterSQL) BuildInclusion(buffer *buffer, filter rel.FilterQuery) { +func (fs FilterSQL) BuildInclusion(buffer *buffer, filter rel.FilterQuery, qs QuerySQL) { var ( values = filter.Value.([]interface{}) ) @@ -105,11 +115,23 @@ func (fs FilterSQL) BuildInclusion(buffer *buffer, filter rel.FilterQuery) { buffer.WriteString(fs.fieldSQL.Build(filter.Field)) if filter.Type == rel.FilterInOp { - buffer.WriteString(" IN (") + buffer.WriteString(" IN ") } else { - buffer.WriteString(" NOT IN (") + buffer.WriteString(" NOT IN ") + } + + fs.buildInclusionValues(buffer, values, qs) +} + +func (fs *FilterSQL) buildInclusionValues(buffer *buffer, values []interface{}, qs QuerySQL) { + if len(values) == 1 { + if value, ok := values[0].(rel.Query); ok { + fs.buildSubQuery(buffer, rel.SubQuery{Query: value}, qs) + return + } } + buffer.WriteByte('(') for i := 0; i < len(values); i++ { if i > 0 { buffer.WriteByte(',') @@ -119,6 +141,13 @@ func (fs FilterSQL) BuildInclusion(buffer *buffer, filter rel.FilterQuery) { buffer.WriteByte(')') } +func (fs FilterSQL) buildSubQuery(buffer *buffer, sub rel.SubQuery, qs QuerySQL) { + buffer.WriteString(sub.Prefix) + buffer.WriteByte('(') + qs.Write(buffer, sub.Query) + buffer.WriteByte(')') +} + // NewFilterSQL builder. func NewFilterSQL(fieldSQL FieldSQL) FilterSQL { return FilterSQL{ diff --git a/mssql.go b/mssql.go index ce8ddc4..5ac4316 100644 --- a/mssql.go +++ b/mssql.go @@ -46,8 +46,8 @@ func new(db *db.DB, tx *db.Tx, savepoint int) rel.Adapter { applyAdapter = NewApplyAdapter(execAdapter, NewApplyTableSQL(fieldSQL, mapColumn), NewApplyIndexSQL(fieldSQL)) insertAdapter = NewInsertAdapter(connectionAdapter, NewInsertSQL(fieldSQL)) insertAllAdapter = NewInsertAllAdapter(connectionAdapter, NewInsertAllSQL(fieldSQL)) - updateAdapter = NewUpdateAdapter(execAdapter, NewUpdateSQL(fieldSQL, filterSQL)) - deleteAdapter = NewDeleteAdapter(execAdapter, NewDeleteSQL(fieldSQL, filterSQL)) + updateAdapter = NewUpdateAdapter(execAdapter, NewUpdateSQL(fieldSQL, querySQL, filterSQL)) + deleteAdapter = NewDeleteAdapter(execAdapter, NewDeleteSQL(fieldSQL, querySQL, filterSQL)) ) return Adapter{ diff --git a/mssql_test.go b/mssql_test.go index 3f120f3..377ae72 100644 --- a/mssql_test.go +++ b/mssql_test.go @@ -38,6 +38,7 @@ func TestAdapter_specs(t *testing.T) { // Query Specs specs.Query(t, repo) specs.QueryJoin(t, repo) + specs.QueryWhereSubQuery(t, repo) specs.QueryNotFound(t, repo) // Preload specs diff --git a/query_sql.go b/query_sql.go index 2a59cf3..2dd2038 100644 --- a/query_sql.go +++ b/query_sql.go @@ -15,19 +15,27 @@ type QuerySQL struct { // Build SQL string and it arguments. func (qs QuerySQL) Build(query rel.Query) (string, []interface{}) { + var buffer buffer + qs.Write(&buffer, query) + buffer.WriteString(";") + + return buffer.String(), buffer.Arguments() +} + +// Write SQL to buffer. +func (qs QuerySQL) Write(buffer *buffer, query rel.Query) { if query.SQLQuery.Statement != "" { - return query.SQLQuery.Statement, query.SQLQuery.Values + buffer.WriteString(query.SQLQuery.Statement) + buffer.AddArguments(query.SQLQuery.Values...) + return } if len(query.SortQuery) == 0 && query.OffsetQuery > 0 && query.LimitQuery > 0 { query = query.Sort("^1") } - var buffer buffer - qs.BuildSelect(&buffer, query.SelectQuery, query.LimitQuery, query.OffsetQuery) - qs.BuildQuery(&buffer, query) - - return buffer.String(), buffer.Arguments() + qs.BuildSelect(buffer, query.SelectQuery, query.LimitQuery, query.OffsetQuery) + qs.BuildQuery(buffer, query) } // BuildSelect SQL to buffer. @@ -77,8 +85,6 @@ func (qs QuerySQL) BuildQuery(buffer *buffer, query rel.Query) { buffer.WriteByte(' ') buffer.WriteString(string(query.LockQuery)) } - - buffer.WriteString(";") } // BuildFrom SQL to buffer. @@ -128,7 +134,7 @@ func (qs QuerySQL) BuildWhere(buffer *buffer, filter rel.FilterQuery) { } buffer.WriteString(" WHERE ") - qs.filterSQL.Write(buffer, filter) + qs.filterSQL.Write(buffer, filter, qs) } // BuildGroupBy SQL to buffer. @@ -152,7 +158,7 @@ func (qs QuerySQL) BuildHaving(buffer *buffer, filter rel.FilterQuery) { } buffer.WriteString(" HAVING ") - qs.filterSQL.Write(buffer, filter) + qs.filterSQL.Write(buffer, filter, qs) } // BuildOrderBy SQL to buffer. diff --git a/update_sql.go b/update_sql.go index 22be1ac..721feb2 100644 --- a/update_sql.go +++ b/update_sql.go @@ -7,6 +7,7 @@ import ( // UpdateSQL builder. type UpdateSQL struct { fieldSQL FieldSQL + querySQL QuerySQL filterSQL FilterSQL } @@ -50,7 +51,7 @@ func (us UpdateSQL) Build(table string, primaryField string, mutates map[string] if !filter.None() { buffer.WriteString(" WHERE ") - us.filterSQL.Write(&buffer, filter) + us.filterSQL.Write(&buffer, filter, us.querySQL) } buffer.WriteString(";") @@ -59,9 +60,10 @@ func (us UpdateSQL) Build(table string, primaryField string, mutates map[string] } // NewUpdateSQL builder. -func NewUpdateSQL(fieldSQL FieldSQL, filterSQL FilterSQL) UpdateSQL { +func NewUpdateSQL(fieldSQL FieldSQL, querySQL QuerySQL, filterSQL FilterSQL) UpdateSQL { return UpdateSQL{ fieldSQL: fieldSQL, + querySQL: querySQL, filterSQL: filterSQL, } }