support ClickHouse Grammar

This commit is contained in:
csuzhang
2022-12-12 17:34:23 +08:00
parent 8e5a46ebd2
commit 561104398a
6 changed files with 68 additions and 4 deletions
+1 -1
View File
@@ -233,7 +233,7 @@ func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interfac
}
default:
switch flavor {
case MySQL, SQLite:
case MySQL, SQLite, ClickHouse:
buf.WriteRune('?')
case PostgreSQL:
fmt.Fprintf(buf, "$%d", len(values)+1)
+9 -1
View File
@@ -16,6 +16,7 @@ const (
PostgreSQL
SQLite
SQLServer
ClickHouse
)
var (
@@ -49,6 +50,8 @@ func (f Flavor) String() string {
return "SQLite"
case SQLServer:
return "SQLServer"
case ClickHouse:
return "ClickHouse"
}
return "<invalid>"
@@ -69,6 +72,8 @@ func (f Flavor) Interpolate(sql string, args []interface{}) (string, error) {
return sqliteInterpolate(sql, args...)
case SQLServer:
return sqlserverInterpolate(sql, args...)
case ClickHouse:
return clickhouseInterpolate(sql, args...)
}
return "", ErrInterpolateNotImplemented
@@ -123,7 +128,7 @@ func (f Flavor) NewUnionBuilder() *UnionBuilder {
// * For PostgreSQL, SQL Server and SQLite, use double quote (") to quote name.
func (f Flavor) Quote(name string) string {
switch f {
case MySQL:
case MySQL, ClickHouse:
return fmt.Sprintf("`%s`", name)
case PostgreSQL, SQLServer, SQLite:
return fmt.Sprintf(`"%s"`, name)
@@ -146,6 +151,9 @@ func (f Flavor) PrepareInsertIgnore(table string, ib *InsertBuilder) {
case SQLite:
// see https://www.sqlite.org/lang_insert.html
ib.verb = "INSERT OR IGNORE"
case ClickHouse:
// see https://clickhouse.tech/docs/en/sql-reference/statements/insert-into/
ib.verb = "INSERT"
default:
// panic if the db flavor is not supported
panic(fmt.Errorf("unsupported db flavor: %s", ib.args.Flavor.String()))
+4
View File
@@ -386,6 +386,10 @@ func sqliteInterpolate(query string, args ...interface{}) (string, error) {
return mysqlLikeInterpolate(SQLite, query, args...)
}
func clickhouseInterpolate(query string, args ...interface{}) (string, error) {
return mysqlLikeInterpolate(ClickHouse, query, args...)
}
func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
switch v := arg.(type) {
case nil:
+46
View File
@@ -163,6 +163,52 @@ func TestFlavorInterpolate(t *testing.T) {
"SELECT @p1", nil,
"", ErrInterpolateMissingArgs,
},
{
MySQL,
"SELECT * FROM a WHERE name = ? AND state IN (?, ?, ?, ?, ?)", []interface{}{"I'm fine", 42, int8(8), int16(-16), int32(32), int64(64)},
"SELECT * FROM a WHERE name = 'I\\'m fine' AND state IN (42, 8, -16, 32, 64)", nil,
},
{
MySQL,
"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"},
"SELECT * FROM `a?` WHERE name = \"?\" AND state IN ('\\r\\n\\b\\t\\Z\\0\\\\\\\"\\'', '?', 42, 8, 16, 32, 64)", nil,
},
{
MySQL,
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?", []interface{}{true, false, float32(1.234567), float64(9.87654321), []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil},
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, _binary'I\\'m bytes', '2019-04-24 12:23:34.123457', '0000-00-00', NULL", nil,
},
{
MySQL,
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\?", []interface{}{MySQL},
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\'MySQL'", nil,
},
{
MySQL,
"SELECT ?", []interface{}{byteArr},
"SELECT _binary'foo'", nil,
},
{
MySQL,
"SELECT ?", nil,
"", ErrInterpolateMissingArgs,
},
{
MySQL,
"SELECT ?", []interface{}{complex(1, 2)},
"", ErrInterpolateUnsupportedArgs,
},
{
MySQL,
"SELECT ?", []interface{}{[]complex128{complex(1, 2)}},
"", ErrInterpolateUnsupportedArgs,
},
{
MySQL,
"SELECT ?", []interface{}{errorValuer(1)},
"", ErrErrorValuer,
},
}
for idx, c := range cases {
+1 -1
View File
@@ -300,7 +300,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
}
switch flavor {
case MySQL, SQLite:
case MySQL, SQLite, ClickHouse:
if sb.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteString(strconv.Itoa(sb.limit))
+7 -1
View File
@@ -112,7 +112,7 @@ func ExampleSelectBuilder_join() {
}
func ExampleSelectBuilder_limit_offset() {
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer}
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer, ClickHouse}
results := make([][]string, len(flavors))
sb := NewSelectBuilder()
saveResults := func() {
@@ -189,6 +189,12 @@ func ExampleSelectBuilder_limit_offset() {
// #2: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS
// #3: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
// #4: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
//
// ClickHouse
// #1: SELECT * FROM user
// #2: SELECT * FROM user
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
// #4: SELECT * FROM user LIMIT 1
}
func ExampleSelectBuilder_ForUpdate() {