Skip to content

Commit f5453c8

Browse files
committed
Merge branch 'hanyuancheung-feature/support_clickhouse_grammar'
2 parents f299327 + f8230fd commit f5453c8

File tree

8 files changed

+96
-8
lines changed

8 files changed

+96
-8
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- [Usage](#usage)
1010
- [Basic usage](#basic-usage)
1111
- [Pre-defined SQL builders](#pre-defined-sql-builders)
12-
- [Build SQL for MySQL, PostgreSQL, SQLServer or SQLite](#build-sql-for-mysql-postgresql-sqlserver-or-sqlite)
12+
- [Build SQL for MySQL, PostgreSQL, SQLServer, SQLite or ClickHouse](#build-sql-for-mysql-postgresql-sqlserve-sqlite-or-clickhouse)
1313
- [Using `Struct` as a light weight ORM](#using-struct-as-a-light-weight-orm)
1414
- [Nested SQL](#nested-sql)
1515
- [Use `sql.Named` in a builder](#use-sqlnamed-in-a-builder)
@@ -110,7 +110,7 @@ Following are some utility methods to deal with special cases.
110110

111111
To learn how to use builders, check out [examples on GoDoc](https://pkg.go.dev/github.com/huandu/go-sqlbuilder#pkg-examples).
112112

113-
### Build SQL for MySQL, PostgreSQL, SQLServer or SQLite
113+
### Build SQL for MySQL, PostgreSQL, SQLServe, SQLite or ClickHouse
114114

115115
Parameter markers are different in MySQL, PostgreSQL, SQLServer and SQLite. This package provides some methods to set the type of markers (we call it "flavor") in all builders.
116116

args.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interfac
233233
}
234234
default:
235235
switch flavor {
236-
case MySQL, SQLite:
236+
case MySQL, SQLite, ClickHouse:
237237
buf.WriteRune('?')
238238
case PostgreSQL:
239239
fmt.Fprintf(buf, "$%d", len(values)+1)

flavor.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
PostgreSQL
1717
SQLite
1818
SQLServer
19+
ClickHouse
1920
)
2021

2122
var (
@@ -49,6 +50,8 @@ func (f Flavor) String() string {
4950
return "SQLite"
5051
case SQLServer:
5152
return "SQLServer"
53+
case ClickHouse:
54+
return "ClickHouse"
5255
}
5356

5457
return "<invalid>"
@@ -69,6 +72,8 @@ func (f Flavor) Interpolate(sql string, args []interface{}) (string, error) {
6972
return sqliteInterpolate(sql, args...)
7073
case SQLServer:
7174
return sqlserverInterpolate(sql, args...)
75+
case ClickHouse:
76+
return clickhouseInterpolate(sql, args...)
7277
}
7378

7479
return "", ErrInterpolateNotImplemented
@@ -119,11 +124,11 @@ func (f Flavor) NewUnionBuilder() *UnionBuilder {
119124
// Quote adds quote for name to make sure the name can be used safely
120125
// as table name or field name.
121126
//
122-
// * For MySQL, use back quote (`) to quote name;
123-
// * For PostgreSQL, SQL Server and SQLite, use double quote (") to quote name.
127+
// - For MySQL, use back quote (`) to quote name;
128+
// - For PostgreSQL, SQL Server and SQLite, use double quote (") to quote name.
124129
func (f Flavor) Quote(name string) string {
125130
switch f {
126-
case MySQL:
131+
case MySQL, ClickHouse:
127132
return fmt.Sprintf("`%s`", name)
128133
case PostgreSQL, SQLServer, SQLite:
129134
return fmt.Sprintf(`"%s"`, name)
@@ -146,6 +151,9 @@ func (f Flavor) PrepareInsertIgnore(table string, ib *InsertBuilder) {
146151
case SQLite:
147152
// see https://www.sqlite.org/lang_insert.html
148153
ib.verb = "INSERT OR IGNORE"
154+
case ClickHouse:
155+
// see https://clickhouse.tech/docs/en/sql-reference/statements/insert-into/
156+
ib.verb = "INSERT"
149157
default:
150158
// panic if the db flavor is not supported
151159
panic(fmt.Errorf("unsupported db flavor: %s", ib.args.Flavor.String()))

insert_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,22 @@ func ExampleInsertBuilder_insertIgnore_sqlite() {
113113
// [1 Huan Du 1 2 Charmy Liu 1 1234567890]
114114
}
115115

116+
func ExampleInsertBuilder_insertIgnore_clickhouse() {
117+
ib := ClickHouse.NewInsertBuilder()
118+
ib.InsertIgnoreInto("demo.user")
119+
ib.Cols("id", "name", "status", "created_at")
120+
ib.Values(1, "Huan Du", 1, Raw("UNIX_TIMESTAMP(NOW())"))
121+
ib.Values(2, "Charmy Liu", 1, 1234567890)
122+
123+
sql, args := ib.Build()
124+
fmt.Println(sql)
125+
fmt.Println(args)
126+
127+
// Output:
128+
// INSERT INTO demo.user (id, name, status, created_at) VALUES (?, ?, ?, UNIX_TIMESTAMP(NOW())), (?, ?, ?, ?)
129+
// [1 Huan Du 1 2 Charmy Liu 1 1234567890]
130+
}
131+
116132
func ExampleInsertBuilder_replaceInto() {
117133
ib := NewInsertBuilder()
118134
ib.ReplaceInto("demo.user")

interpolate.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ func sqliteInterpolate(query string, args ...interface{}) (string, error) {
386386
return mysqlLikeInterpolate(SQLite, query, args...)
387387
}
388388

389+
func clickhouseInterpolate(query string, args ...interface{}) (string, error) {
390+
return mysqlLikeInterpolate(ClickHouse, query, args...)
391+
}
392+
389393
func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
390394
switch v := arg.(type) {
391395
case nil:
@@ -421,6 +425,9 @@ func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
421425

422426
case SQLServer:
423427
buf = append(buf, v.Format("2006-01-02 15:04:05.999999 Z07:00")...)
428+
429+
case ClickHouse:
430+
buf = append(buf, v.Format("2006-01-02 15:04:05.999999")...)
424431
}
425432

426433
buf = append(buf, '\'')
@@ -521,6 +528,11 @@ func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
521528
case SQLServer:
522529
buf = append(buf, "0x"...)
523530
buf = appendHex(buf, data)
531+
532+
case ClickHouse:
533+
buf = append(buf, "unhex('"...)
534+
buf = appendHex(buf, data)
535+
buf = append(buf, "')"...)
524536
}
525537

526538
default:

interpolate_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,52 @@ func TestFlavorInterpolate(t *testing.T) {
163163
"SELECT @p1", nil,
164164
"", ErrInterpolateMissingArgs,
165165
},
166+
167+
{
168+
ClickHouse,
169+
"SELECT * FROM a WHERE name = ? AND state IN (?, ?, ?, ?, ?)", []interface{}{"I'm fine", 42, int8(8), int16(-16), int32(32), int64(64)},
170+
"SELECT * FROM a WHERE name = 'I\\'m fine' AND state IN (42, 8, -16, 32, 64)", nil,
171+
},
172+
{
173+
ClickHouse,
174+
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN (?, '?', ?, ?, ?, ?, ?)", []interface{}{"\r\n\b\t\x1a\x00\\\"'", uint(42), uint8(8), uint16(16), uint32(32), uint64(64), "useless"},
175+
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN ('\\r\\n\\b\\t\\Z\\0\\\\\\\"\\'', '?', 42, 8, 16, 32, 64)", nil,
176+
},
177+
{
178+
ClickHouse,
179+
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?", []interface{}{true, false, float32(1.234567), 9.87654321, []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil},
180+
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, unhex('49276D206279746573'), '2019-04-24 12:23:34.123457', '0000-00-00', NULL", nil,
181+
},
182+
{
183+
ClickHouse,
184+
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\?", []interface{}{MySQL},
185+
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\'MySQL'", nil,
186+
},
187+
{
188+
ClickHouse,
189+
"SELECT ?", []interface{}{byteArr},
190+
"SELECT unhex('666F6F')", nil,
191+
},
192+
{
193+
ClickHouse,
194+
"SELECT ?", nil,
195+
"", ErrInterpolateMissingArgs,
196+
},
197+
{
198+
ClickHouse,
199+
"SELECT ?", []interface{}{complex(1, 2)},
200+
"", ErrInterpolateUnsupportedArgs,
201+
},
202+
{
203+
ClickHouse,
204+
"SELECT ?", []interface{}{[]complex128{complex(1, 2)}},
205+
"", ErrInterpolateUnsupportedArgs,
206+
},
207+
{
208+
ClickHouse,
209+
"SELECT ?", []interface{}{errorValuer(1)},
210+
"", ErrErrorValuer,
211+
},
166212
}
167213

168214
for idx, c := range cases {

select.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
300300
}
301301

302302
switch flavor {
303-
case MySQL, SQLite:
303+
case MySQL, SQLite, ClickHouse:
304304
if sb.limit >= 0 {
305305
buf.WriteString(" LIMIT ")
306306
buf.WriteString(strconv.Itoa(sb.limit))

select_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func ExampleSelectBuilder_join() {
112112
}
113113

114114
func ExampleSelectBuilder_limit_offset() {
115-
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer}
115+
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer, ClickHouse}
116116
results := make([][]string, len(flavors))
117117
sb := NewSelectBuilder()
118118
saveResults := func() {
@@ -189,6 +189,12 @@ func ExampleSelectBuilder_limit_offset() {
189189
// #2: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS
190190
// #3: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
191191
// #4: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
192+
//
193+
// ClickHouse
194+
// #1: SELECT * FROM user
195+
// #2: SELECT * FROM user
196+
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
197+
// #4: SELECT * FROM user LIMIT 1
192198
}
193199

194200
func ExampleSelectBuilder_ForUpdate() {

0 commit comments

Comments
 (0)