mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
refs #98 add new flavor Presto
This commit is contained in:
@@ -114,7 +114,7 @@ To learn how to use builders, check out [examples on GoDoc](https://pkg.go.dev/g
|
||||
|
||||
SQL syntax and parameter marks vary in different systems. In this package, we introduce a concept called "flavor" to smooth out these difference.
|
||||
|
||||
Right now, MySQL, PostgreSQL, SQLServer, SQLite, CQL and ClickHouse are defined in flavor list. Feel free to open issue or send pull request if anyone asks for a new flavor.
|
||||
Right now, `MySQL`, `PostgreSQL`, `SQLServer`, `SQLite`, `CQL`, `ClickHouse` and `Presto` are defined in flavor list. Feel free to open issue or send pull request if anyone asks for a new flavor.
|
||||
|
||||
By default, all builders uses `DefaultFlavor` to build SQL. The default value is `MySQL`.
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interfac
|
||||
}
|
||||
default:
|
||||
switch flavor {
|
||||
case MySQL, SQLite, CQL, ClickHouse:
|
||||
case MySQL, SQLite, CQL, ClickHouse, Presto:
|
||||
buf.WriteRune('?')
|
||||
case PostgreSQL:
|
||||
fmt.Fprintf(buf, "$%d", len(values)+1)
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
SQLServer
|
||||
CQL
|
||||
ClickHouse
|
||||
Presto
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,6 +56,8 @@ func (f Flavor) String() string {
|
||||
return "CQL"
|
||||
case ClickHouse:
|
||||
return "ClickHouse"
|
||||
case Presto:
|
||||
return "Presto"
|
||||
}
|
||||
|
||||
return "<invalid>"
|
||||
@@ -79,6 +82,8 @@ func (f Flavor) Interpolate(sql string, args []interface{}) (string, error) {
|
||||
return cqlInterpolate(sql, args...)
|
||||
case ClickHouse:
|
||||
return clickhouseInterpolate(sql, args...)
|
||||
case Presto:
|
||||
return prestoInterpolate(sql, args...)
|
||||
}
|
||||
|
||||
return "", ErrInterpolateNotImplemented
|
||||
@@ -135,7 +140,7 @@ func (f Flavor) Quote(name string) string {
|
||||
switch f {
|
||||
case MySQL, ClickHouse:
|
||||
return fmt.Sprintf("`%s`", name)
|
||||
case PostgreSQL, SQLServer, SQLite:
|
||||
case PostgreSQL, SQLServer, SQLite, Presto:
|
||||
return fmt.Sprintf(`"%s"`, name)
|
||||
case CQL:
|
||||
return fmt.Sprintf("'%s'", name)
|
||||
@@ -149,18 +154,22 @@ func (f Flavor) PrepareInsertIgnore(table string, ib *InsertBuilder) {
|
||||
switch ib.args.Flavor {
|
||||
case MySQL:
|
||||
ib.verb = "INSERT IGNORE"
|
||||
|
||||
case PostgreSQL:
|
||||
// see https://www.postgresql.org/docs/current/sql-insert.html
|
||||
ib.verb = "INSERT"
|
||||
// add sql statement at the end after values, i.e. INSERT INTO ... ON CONFLICT DO NOTHING
|
||||
ib.marker = insertMarkerAfterValues
|
||||
ib.SQL("ON CONFLICT DO NOTHING")
|
||||
|
||||
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/
|
||||
|
||||
case ClickHouse, CQL, SQLServer, Presto:
|
||||
// All other databases do not support insert ignore
|
||||
ib.verb = "INSERT"
|
||||
|
||||
default:
|
||||
// panic if the db flavor is not supported
|
||||
panic(fmt.Errorf("unsupported db flavor: %s", ib.args.Flavor.String()))
|
||||
|
||||
@@ -395,6 +395,10 @@ func clickhouseInterpolate(query string, args ...interface{}) (string, error) {
|
||||
return mysqlLikeInterpolate(ClickHouse, query, args...)
|
||||
}
|
||||
|
||||
func prestoInterpolate(query string, args ...interface{}) (string, error) {
|
||||
return mysqlLikeInterpolate(Presto, query, args...)
|
||||
}
|
||||
|
||||
func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
|
||||
switch v := arg.(type) {
|
||||
case nil:
|
||||
@@ -436,6 +440,9 @@ func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
|
||||
|
||||
case ClickHouse:
|
||||
buf = append(buf, v.Format("2006-01-02 15:04:05.999999")...)
|
||||
|
||||
case Presto:
|
||||
buf = append(buf, v.Format("2006-01-02 15:04:05.000")...)
|
||||
}
|
||||
|
||||
buf = append(buf, '\'')
|
||||
@@ -541,6 +548,11 @@ func encodeValue(buf []byte, arg interface{}, flavor Flavor) ([]byte, error) {
|
||||
buf = append(buf, "unhex('"...)
|
||||
buf = appendHex(buf, data)
|
||||
buf = append(buf, "')"...)
|
||||
|
||||
case Presto:
|
||||
buf = append(buf, "from_hex('"...)
|
||||
buf = appendHex(buf, data)
|
||||
buf = append(buf, "')"...)
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -238,6 +238,51 @@ func TestFlavorInterpolate(t *testing.T) {
|
||||
"SELECT ?", []interface{}{errorValuer(1)},
|
||||
"", ErrErrorValuer,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"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,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"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,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?", []interface{}{true, false, float32(1.234567), 9.87654321, []byte(nil), []byte("I'm bytes"), dt, time.Time{}, nil},
|
||||
"SELECT TRUE, FALSE, 1.234567, 9.87654321, NULL, from_hex('49276D206279746573'), '2019-04-24 12:23:34.123', '0000-00-00', NULL", nil,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\?", []interface{}{MySQL},
|
||||
"SELECT '\\'?', \"\\\"?\", `\\`?`, \\'MySQL'", nil,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?", []interface{}{byteArr},
|
||||
"SELECT from_hex('666F6F')", nil,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?", nil,
|
||||
"", ErrInterpolateMissingArgs,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?", []interface{}{complex(1, 2)},
|
||||
"", ErrInterpolateUnsupportedArgs,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?", []interface{}{[]complex128{complex(1, 2)}},
|
||||
"", ErrInterpolateUnsupportedArgs,
|
||||
},
|
||||
{
|
||||
Presto,
|
||||
"SELECT ?", []interface{}{errorValuer(1)},
|
||||
"", ErrErrorValuer,
|
||||
},
|
||||
}
|
||||
|
||||
for idx, c := range cases {
|
||||
|
||||
@@ -317,7 +317,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
}
|
||||
case PostgreSQL:
|
||||
case PostgreSQL, Presto:
|
||||
if sb.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
|
||||
+7
-1
@@ -112,7 +112,7 @@ func ExampleSelectBuilder_join() {
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_limit_offset() {
|
||||
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer, CQL, ClickHouse}
|
||||
flavors := []Flavor{MySQL, PostgreSQL, SQLite, SQLServer, CQL, ClickHouse, Presto}
|
||||
results := make([][]string, len(flavors))
|
||||
sb := NewSelectBuilder()
|
||||
saveResults := func() {
|
||||
@@ -203,6 +203,12 @@ func ExampleSelectBuilder_limit_offset() {
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
//
|
||||
// Presto
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user OFFSET 0
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_ForUpdate() {
|
||||
|
||||
Reference in New Issue
Block a user