Skip to content

Commit 06e2f31

Browse files
committed
added composite in support delegation
1 parent cec446e commit 06e2f31

5 files changed

Lines changed: 144 additions & 4 deletions

File tree

metadata/info/composite.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package info
2+
3+
import "strings"
4+
5+
// CompositeIn renders a dialect-aware composite IN predicate using generic '?'
6+
// placeholders. Placeholder numbering, when needed, is applied later by EnsurePlaceholders.
7+
func (d *Dialect) CompositeIn(columns []string, rowCount int) string {
8+
if len(columns) == 0 || rowCount <= 0 {
9+
return "1 = 0"
10+
}
11+
12+
if len(columns) == 1 {
13+
return scalarIn(columns[0], rowCount)
14+
}
15+
16+
if d != nil && d.CompositeInRenderer != nil {
17+
return d.CompositeInRenderer(columns, rowCount)
18+
}
19+
return defaultCompositeIn(columns, rowCount)
20+
}
21+
22+
func scalarIn(column string, rowCount int) string {
23+
if strings.TrimSpace(column) == "" || rowCount <= 0 {
24+
return "1 = 0"
25+
}
26+
builder := &strings.Builder{}
27+
builder.WriteString(column)
28+
builder.WriteString(" IN (")
29+
for i := 0; i < rowCount; i++ {
30+
if i > 0 {
31+
builder.WriteString(", ")
32+
}
33+
builder.WriteByte('?')
34+
}
35+
builder.WriteByte(')')
36+
return builder.String()
37+
}
38+
39+
func defaultCompositeIn(columns []string, rowCount int) string {
40+
builder := &strings.Builder{}
41+
builder.WriteByte('(')
42+
builder.WriteString(strings.Join(columns, ", "))
43+
builder.WriteString(") IN (")
44+
for row := 0; row < rowCount; row++ {
45+
if row > 0 {
46+
builder.WriteString(", ")
47+
}
48+
builder.WriteByte('(')
49+
for col := range columns {
50+
if col > 0 {
51+
builder.WriteString(", ")
52+
}
53+
builder.WriteByte('?')
54+
}
55+
builder.WriteByte(')')
56+
}
57+
builder.WriteByte(')')
58+
return builder.String()
59+
}

metadata/info/composite_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package info
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/viant/sqlx/metadata/database"
8+
)
9+
10+
func TestDialect_CompositeIn(t *testing.T) {
11+
testCases := []struct {
12+
description string
13+
dialect *Dialect
14+
columns []string
15+
rowCount int
16+
expect string
17+
}{
18+
{
19+
description: "mysql composite",
20+
dialect: &Dialect{Product: database.Product{Name: "MySQL"}},
21+
columns: []string{"t.advertiser_id", "t.val"},
22+
rowCount: 2,
23+
expect: "(t.advertiser_id, t.val) IN ((?, ?), (?, ?))",
24+
},
25+
{
26+
description: "postgres composite",
27+
dialect: &Dialect{Product: database.Product{Name: "PostgreSQL"}},
28+
columns: []string{"t.advertiser_id", "t.val"},
29+
rowCount: 2,
30+
expect: "(t.advertiser_id, t.val) IN ((?, ?), (?, ?))",
31+
},
32+
{
33+
description: "bigquery composite",
34+
dialect: &Dialect{Product: database.Product{Name: "BigQuery"}, CompositeInRenderer: func(columns []string, rowCount int) string {
35+
return "(t.advertiser_id, t.val) IN (SELECT AS STRUCT ?, ? UNION ALL SELECT AS STRUCT ?, ?)"
36+
}},
37+
columns: []string{"t.advertiser_id", "t.val"},
38+
rowCount: 2,
39+
expect: "(t.advertiser_id, t.val) IN (SELECT AS STRUCT ?, ? UNION ALL SELECT AS STRUCT ?, ?)",
40+
},
41+
{
42+
description: "empty rows",
43+
dialect: &Dialect{Product: database.Product{Name: "MySQL"}},
44+
columns: []string{"t.advertiser_id", "t.val"},
45+
rowCount: 0,
46+
expect: "1 = 0",
47+
},
48+
}
49+
50+
for _, testCase := range testCases {
51+
t.Run(testCase.description, func(t *testing.T) {
52+
assert.Equal(t, testCase.expect, testCase.dialect.CompositeIn(testCase.columns, testCase.rowCount))
53+
})
54+
}
55+
}

metadata/info/dialect.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import (
77
"strings"
88
)
99

10-
//Dialect represents dialect
10+
// Dialect represents dialect
1111
type Dialect struct {
1212
database.Product
1313
Placeholder string // prepare statement placeholder, default '?', but oracle uses ':'
1414
PlaceholderResolver placeholder.Generator
15+
CompositeInRenderer func(columns []string, rowCount int) string
1516
Transactional bool
1617
Insert dialect.InsertFeatures
1718
Upsert dialect.UpsertFeatures
@@ -29,18 +30,18 @@ type Dialect struct {
2930
SpecialKeywordEscapeQuote byte
3031
}
3132

32-
//Dialects represents dialects
33+
// Dialects represents dialects
3334
type Dialects []*Dialect
3435

35-
//PlaceholderGetter returns PlaceholderResolver if not nil, otherwise returns function that returns Placeholder
36+
// PlaceholderGetter returns PlaceholderResolver if not nil, otherwise returns function that returns Placeholder
3637
func (d *Dialect) PlaceholderGetter() func() string {
3738
if d.PlaceholderResolver != nil {
3839
return d.PlaceholderResolver.Resolver()
3940
}
4041
return (&placeholder.DefaultGenerator{}).Resolver()
4142
}
4243

43-
//EnsurePlaceholders converts '?' to specific dialect placeholders if needed
44+
// EnsurePlaceholders converts '?' to specific dialect placeholders if needed
4445
func (d *Dialect) EnsurePlaceholders(SQL string) string {
4546
if d.Placeholder == placeholder.Default {
4647
return SQL
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package bigquery
2+
3+
import "strings"
4+
5+
func compositeIn(columns []string, rowCount int) string {
6+
builder := &strings.Builder{}
7+
builder.WriteByte('(')
8+
builder.WriteString(strings.Join(columns, ", "))
9+
builder.WriteString(") IN (")
10+
for row := 0; row < rowCount; row++ {
11+
if row > 0 {
12+
builder.WriteString(" UNION ALL ")
13+
}
14+
builder.WriteString("SELECT AS STRUCT ")
15+
for col := range columns {
16+
if col > 0 {
17+
builder.WriteString(", ")
18+
}
19+
builder.WriteByte('?')
20+
}
21+
}
22+
builder.WriteByte(')')
23+
return builder.String()
24+
}

metadata/product/bigquery/meta.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ SESSION_USER() AS USER_NAME,
200200
registry.RegisterDialect(&info.Dialect{
201201
Product: bigQuery,
202202
Placeholder: "?",
203+
CompositeInRenderer: compositeIn,
203204
Transactional: false, //only script is transactional
204205
Insert: dialect.InsertWithMultiValues,
205206
Upsert: dialect.UpsertTypeMerge,

0 commit comments

Comments
 (0)