From 78da8d3c7d8223463b53da8bfd437c7f501acc05 Mon Sep 17 00:00:00 2001 From: a Date: Sun, 19 Mar 2023 03:19:33 -0500 Subject: [PATCH 1/3] move ? --- internal/sqlbuilder/builder.go | 2 +- internal/sqlbuilder/select.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/sqlbuilder/builder.go b/internal/sqlbuilder/builder.go index b9cf5799..42cf6729 100644 --- a/internal/sqlbuilder/builder.go +++ b/internal/sqlbuilder/builder.go @@ -376,7 +376,7 @@ func columnFragments(columns []interface{}) ([]exql.Fragment, []interface{}, err if len(fnArgs) == 0 { fnName = fnName + "()" } else { - fnName = fnName + "(?" + strings.Repeat("?, ", len(fnArgs)-1) + ")" + fnName = fnName + "(" + strings.Repeat("?, ", len(fnArgs)-1) + "?)" } fnName, fnArgs = Preprocess(fnName, fnArgs) f[i] = &exql.Raw{Value: fnName} diff --git a/internal/sqlbuilder/select.go b/internal/sqlbuilder/select.go index 93772405..0fa90011 100644 --- a/internal/sqlbuilder/select.go +++ b/internal/sqlbuilder/select.go @@ -265,7 +265,7 @@ func (sel *selector) OrderBy(columns ...interface{}) db.Selector { if len(fnArgs) == 0 { fnName = fnName + "()" } else { - fnName = fnName + "(?" + strings.Repeat("?, ", len(fnArgs)-1) + ")" + fnName = fnName + "(" + strings.Repeat("?, ", len(fnArgs)-1) + "?)" } fnName, fnArgs = Preprocess(fnName, fnArgs) sort = &exql.SortColumn{ From 241dcdbf1a332442cca7e6a58ed5affc79088779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Nieto?= Date: Sat, 2 Sep 2023 13:36:54 -0600 Subject: [PATCH 2/3] fix FuncExpr expansion --- internal/sqlbuilder/builder.go | 2 +- internal/sqlbuilder/builder_test.go | 26 ++++++++++++++++++++++++++ internal/sqlbuilder/select.go | 2 +- internal/sqlbuilder/template.go | 29 ++++++++++++++++++++--------- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/internal/sqlbuilder/builder.go b/internal/sqlbuilder/builder.go index 42cf6729..c3f494c7 100644 --- a/internal/sqlbuilder/builder.go +++ b/internal/sqlbuilder/builder.go @@ -376,7 +376,7 @@ func columnFragments(columns []interface{}) ([]exql.Fragment, []interface{}, err if len(fnArgs) == 0 { fnName = fnName + "()" } else { - fnName = fnName + "(" + strings.Repeat("?, ", len(fnArgs)-1) + "?)" + fnName = fnName + "(?" + strings.Repeat(", ?", len(fnArgs)-1) + ")" } fnName, fnArgs = Preprocess(fnName, fnArgs) f[i] = &exql.Raw{Value: fnName} diff --git a/internal/sqlbuilder/builder_test.go b/internal/sqlbuilder/builder_test.go index ce123909..4842b389 100644 --- a/internal/sqlbuilder/builder_test.go +++ b/internal/sqlbuilder/builder_test.go @@ -873,6 +873,32 @@ func TestSelect(t *testing.T) { sel.Arguments(), ) + { + sel := b.Select(db.Func("FOO", "A", "B", 1)).From("accounts").Where(db.Cond{"time": db.Func("FUNCTION", "20170103", "YYYYMMDD", 1, "E")}) + + assert.Equal( + `SELECT FOO($1, $2, $3) FROM "accounts" WHERE ("time" = FUNCTION($4, $5, $6, $7))`, + sel.String(), + ) + assert.Equal( + []interface{}{"A", "B", 1, "20170103", "YYYYMMDD", 1, "E"}, + sel.Arguments(), + ) + } + + { + + sel := b.Select(db.Func("FOO", "A", "B", 1)).From("accounts").Where(db.Cond{db.Func("FUNCTION", "20170103", "YYYYMMDD", 1, "E"): db.Func("FOO", 1)}) + + assert.Equal( + `SELECT FOO($1, $2, $3) FROM "accounts" WHERE (FUNCTION($4, $5, $6, $7) = FOO($8))`, + sel.String(), + ) + assert.Equal( + []interface{}{"A", "B", 1, "20170103", "YYYYMMDD", 1, "E", 1}, + sel.Arguments(), + ) + } } } diff --git a/internal/sqlbuilder/select.go b/internal/sqlbuilder/select.go index 0fa90011..0e59ac78 100644 --- a/internal/sqlbuilder/select.go +++ b/internal/sqlbuilder/select.go @@ -265,7 +265,7 @@ func (sel *selector) OrderBy(columns ...interface{}) db.Selector { if len(fnArgs) == 0 { fnName = fnName + "()" } else { - fnName = fnName + "(" + strings.Repeat("?, ", len(fnArgs)-1) + "?)" + fnName = fnName + "(?" + strings.Repeat(", ?", len(fnArgs)-1) + ")" } fnName, fnArgs = Preprocess(fnName, fnArgs) sort = &exql.SortColumn{ diff --git a/internal/sqlbuilder/template.go b/internal/sqlbuilder/template.go index 1ae54ced..4a5fa8ad 100644 --- a/internal/sqlbuilder/template.go +++ b/internal/sqlbuilder/template.go @@ -161,20 +161,31 @@ func (tu *templateWithUtils) toColumnValues(term interface{}) (cv exql.ColumnVal case adapter.Constraint: columnValue := exql.ColumnValue{} - // Getting column and operator. - if column, ok := t.Key().(string); ok { - chunks := strings.SplitN(strings.TrimSpace(column), " ", 2) + // TODO: Key and Value are similar. Can we refactor this? Maybe think about + // Left/Right rather than Key/Value. + + switch key := t.Key().(type) { + case string: + chunks := strings.SplitN(strings.TrimSpace(key), " ", 2) columnValue.Column = exql.ColumnWithName(chunks[0]) if len(chunks) > 1 { columnValue.Operator = chunks[1] } - } else { - if rawValue, ok := t.Key().(*adapter.RawExpr); ok { - columnValue.Column = &exql.Raw{Value: rawValue.Raw()} - args = append(args, rawValue.Arguments()...) + case *adapter.RawExpr: + columnValue.Column = &exql.Raw{Value: key.Raw()} + args = append(args, key.Arguments()...) + case *db.FuncExpr: + fnName, fnArgs := key.Name(), key.Arguments() + if len(fnArgs) == 0 { + fnName = fnName + "()" } else { - columnValue.Column = &exql.Raw{Value: fmt.Sprintf("%v", t.Key())} + fnName = fnName + "(?" + strings.Repeat(", ?", len(fnArgs)-1) + ")" } + fnName, fnArgs = Preprocess(fnName, fnArgs) + columnValue.Column = &exql.Raw{Value: fnName} + args = append(args, fnArgs...) + default: + columnValue.Column = &exql.Raw{Value: fmt.Sprintf("%v", key)} } switch value := t.Value().(type) { @@ -185,7 +196,7 @@ func (tu *templateWithUtils) toColumnValues(term interface{}) (cv exql.ColumnVal fnName = fnName + "()" } else { // A function with one or more arguments. - fnName = fnName + "(?" + strings.Repeat("?, ", len(fnArgs)-1) + ")" + fnName = fnName + "(?" + strings.Repeat(", ?", len(fnArgs)-1) + ")" } fnName, fnArgs = Preprocess(fnName, fnArgs) columnValue.Value = &exql.Raw{Value: fnName} From 5025ae3f2b4c5434266caa06f56da2c946806d24 Mon Sep 17 00:00:00 2001 From: Jurijs Girtakovskis Date: Sun, 5 Nov 2023 06:20:43 -0600 Subject: [PATCH 3/3] Cockroach delete limit (#697) * DELETE FROM must have LIMIT support CockroachDB is not able to deal well with massive deletes and their recommended approach is to batch delete statements. Thus, support for LIMIT statement is a must in the adapter. * Updated tests --- adapter/cockroachdb/template.go | 3 +++ adapter/cockroachdb/template_test.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/adapter/cockroachdb/template.go b/adapter/cockroachdb/template.go index 9d47fed7..94fe5a15 100644 --- a/adapter/cockroachdb/template.go +++ b/adapter/cockroachdb/template.go @@ -123,6 +123,9 @@ const ( DELETE FROM {{.Table | compile}} {{.Where | compile}} + {{if .Limit}} + LIMIT {{.Limit}} + {{end}} ` adapterUpdateLayout = ` UPDATE diff --git a/adapter/cockroachdb/template_test.go b/adapter/cockroachdb/template_test.go index 129449dc..de3b1bbe 100644 --- a/adapter/cockroachdb/template_test.go +++ b/adapter/cockroachdb/template_test.go @@ -259,4 +259,9 @@ func TestTemplateDelete(t *testing.T) { `DELETE FROM "artist" WHERE (id > 5)`, b.DeleteFrom("artist").Where("id > 5").String(), ) + + assert.Equal( + `DELETE FROM "artist" WHERE (id > 5) LIMIT 10`, + b.DeleteFrom("artist").Where("id > 5").Limit(10).String(), + ) }