Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clickhouse support #264

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
949a4bc
Add clickhouse support
pablomatiasgomez Jun 19, 2024
148d9d6
Rollback imports
pablomatiasgomez Jun 19, 2024
7a954c0
fixed conflicts
pablomatiasgomez Jun 19, 2024
77ae667
go mod tidy
pablomatiasgomez Jun 19, 2024
ef5c7ff
Fix tests
pablomatiasgomez Jun 19, 2024
58a8d6d
Fix README
pablomatiasgomez Jun 19, 2024
67439d9
Support UPDATE in clickhouse
pablomatiasgomez Jun 19, 2024
d9fdcd3
trigger build?
pablomatiasgomez Jun 19, 2024
0d648a5
Use go 1.21
pablomatiasgomez Jun 19, 2024
3b68388
Use updated image from circleci
pablomatiasgomez Jun 19, 2024
100f008
No need for working directory
pablomatiasgomez Jun 19, 2024
ef4add6
Fix clickhouse test
pablomatiasgomez Jun 19, 2024
6b42018
Add more test coverage
pablomatiasgomez Jun 19, 2024
270e1bb
Rename Clickhouse to ClickHouse
pablomatiasgomez Jun 20, 2024
c994f0e
Code review update
pablomatiasgomez Jun 20, 2024
c6ae5ce
Remove CombinedOffset support, not needed.
pablomatiasgomez Jun 21, 2024
aecb6d2
Remove not needed join types
pablomatiasgomez Jun 21, 2024
117f0e5
Remove SupportsOn
pablomatiasgomez Jun 21, 2024
06c69f4
Fix test
pablomatiasgomez Jun 21, 2024
1e24781
Merge branch 'master' of github.com:gocraft/dbr into pablomatiasgomez…
pablomatiasgomez Jun 21, 2024
1c983ea
Merge branch 'master' of github.com:gocraft/dbr into pablomatiasgomez…
pablomatiasgomez Jul 8, 2024
25166f6
Remove OnConflict that was moved to a separate PR
pablomatiasgomez Jul 8, 2024
39e2be0
Update go driver to 2.26.0
pablomatiasgomez Jul 8, 2024
9ce30c2
Revert test changes that were moved to a different PR
pablomatiasgomez Jul 8, 2024
ac0d27c
Fix branch test
pablomatiasgomez Jul 8, 2024
3567762
Add back SupportsOn
pablomatiasgomez Jul 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ jobs:
- DBR_TEST_MYSQL_DSN=root:root@tcp(127.0.0.1:3306)/circle_test
- DBR_TEST_POSTGRES_DSN=postgres://postgres:[email protected]:5432/postgres?sslmode=disable
- DBR_TEST_MSSQL_DSN=sqlserver://sa:[email protected]/?database=master
- DBR_TEST_CLICKHOUSE_DSN=clickhouse://127.0.0.1:9000
- GO111MODULE=on
- FOSSA_API_KEY=486be1298513b278ba3916aa63975d7c
- image: percona:5.7
Expand All @@ -20,6 +21,9 @@ jobs:
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=qwe123QWE
- image: clickhouse/clickhouse-server:23.8.15
environment:
- CLICKHOUSE_DO_NOT_CHOWN=1
working_directory: /go/src/github.com/gocraft/dbr
steps:
- run: |
Expand All @@ -28,6 +32,6 @@ jobs:
- run: fossa analyze
- run:
name: Wait for db
command: dockerize -wait tcp://127.0.0.1:3306 -wait tcp://127.0.0.1:5432 -wait tcp://127.0.0.1:1433 -timeout 1m
command: dockerize -wait tcp://127.0.0.1:3306 -wait tcp://127.0.0.1:5432 -wait tcp://127.0.0.1:1433 -wait tcp://127.0.0.1:9000 -timeout 1m
- run: go test -v -cover -bench . ./...
- run: fossa test
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Mac Things
.DS_Store
# Goland things
.idea/
.vscode/
# Emacs
*~
# Local development directories
tmp/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import "github.com/gocraft/dbr/v2"
* PostgreSQL
* SQLite3
* MsSQL
* Clickhouse
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved

## Examples

Expand Down Expand Up @@ -161,6 +162,7 @@ BenchmarkLoadValues/dbr_100000-8 10 147202536 ns/op 23680625 B/op
Inspiration from these excellent libraries:
* [sqlx](https://github.com/jmoiron/sqlx) - various useful tools and utils for interacting with database/sql.
* [Squirrel](https://github.com/lann/squirrel) - simple fluent query builder.
* [mailru/dbr](https://github.com/mailru/dbr) - upsert functionality shamelessly and gratefully adapted from this lib.

Authors:
* Jonathan Novak -- [https://github.com/cypriss](https://github.com/cypriss)
Expand Down
2 changes: 2 additions & 0 deletions README.md.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import "github.com/gocraft/dbr/v2"
* PostgreSQL
* SQLite3
* MsSQL
* Clickhouse
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved

## Examples

Expand Down Expand Up @@ -78,6 +79,7 @@ BenchmarkLoadValues/dbr_100000-8 10 147202536 ns/op 23680625 B/op
Inspiration from these excellent libraries:
* [sqlx](https://github.com/jmoiron/sqlx) - various useful tools and utils for interacting with database/sql.
* [Squirrel](https://github.com/lann/squirrel) - simple fluent query builder.
* [mailru/dbr](https://github.com/mailru/dbr) - upsert functionality shamelessly and gratefully adapted from this lib.

Authors:
* Jonathan Novak -- [https://github.com/cypriss](https://github.com/cypriss)
Expand Down
86 changes: 86 additions & 0 deletions branch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package dbr

// When creates a WHEN statement given a condition and a value that's evaluated
// if the condition is true.
func When(cond Builder, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
buf.WriteString("WHEN (")
err := cond.Build(d, buf)
if err != nil {
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved
return err
}
buf.WriteString(") THEN ")

builder, ok := value.(Builder)
if ok {
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved
err = builder.Build(d, buf)
if err != nil {
return err
}
return nil
}

buf.WriteString(placeholder)
buf.WriteValue(value)
return nil
})
}

// Else creates an ELSE statement given a value or a condition.
func Else(value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
buf.WriteString("ELSE ")
if builder, ok := value.(Builder); ok {
if err := builder.Build(d, buf); err != nil {
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved
return err
}
return nil
}
buf.WriteString(placeholder)
buf.WriteValue(value)
return nil
})
}

// Builder interface that includes AS for asliasing CASE statements.
pablomatiasgomez marked this conversation as resolved.
Show resolved Hide resolved
type CaseBuilder interface {
Builder
As(name string) Builder
}

// CaseBuildFunc implements Builder.
type CaseBuildFunc func(Dialect, Buffer) error

// Case creates a CASE statement from a list of conditions.
// If there are more than 1 conditions, the last one will be an else statement.
func Case(conds ...Builder) CaseBuilder {
return CaseBuildFunc(func(d Dialect, buf Buffer) error {
buf.WriteString("CASE ")
l := len(conds)
for i, cond := range conds {
cond.Build(d, buf)
if i < l-1 {
buf.WriteString(" ")
}
}
buf.WriteString(" END")
return nil
})
}

// AS adds an alias to the CASE statement.
func (cb CaseBuildFunc) As(alias string) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
if err := cb(d, buf); err != nil {
return err
}
buf.WriteString(" AS ")
buf.WriteString(d.QuoteIdent(alias))
return nil
})
}

// Build calls itself to build SQL.
func (cb CaseBuildFunc) Build(d Dialect, buf Buffer) error {
return cb(d, buf)
}
88 changes: 88 additions & 0 deletions branch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package dbr

import (
"testing"

"github.com/gocraft/dbr/v2/dialect"
"github.com/stretchr/testify/require"
)

func TestWhen(t *testing.T) {
for _, test := range []struct {
when Builder
query string
value []interface{}
}{
{
when: When(Eq("col", 1), 1),
query: "WHEN (`col` = ?) THEN ?",
value: []interface{}{1, 1},
},
{
when: When(And(Gt("a", 1), Lt("b", 2)), "c"),
query: "WHEN ((`a` > ?) AND (`b` < ?)) THEN ?",
value: []interface{}{1, 2, "c"},
},
{
when: When(Eq("a", 1), Gt("b", 2)),
query: "WHEN (`a` = ?) THEN `b` > ?",
value: []interface{}{1, 2},
},
} {
buf := NewBuffer()
err := test.when.Build(dialect.Clickhouse, buf)
require.NoError(t, err)
require.Equal(t, test.query, buf.String())
require.Equal(t, test.value, buf.Value())
}
}

func TestCase(t *testing.T) {
for _, test := range []struct {
caseBuilder Builder
query string
value []interface{}
}{
{
caseBuilder: Case(When(Eq("col", 1), 1)),
query: "CASE WHEN (`col` = ?) THEN ? END",
value: []interface{}{1, 1},
},
{
caseBuilder: Case(When(Eq("col", 1), 1)).As("a"),
query: "CASE WHEN (`col` = ?) THEN ? END AS `a`",
value: []interface{}{1, 1},
},
{
caseBuilder: Case(When(Eq("col", 1), 2), Else(3)),
query: "CASE WHEN (`col` = ?) THEN ? ELSE ? END",
value: []interface{}{1, 2, 3},
},
{
caseBuilder: Case(When(Eq("a", 1), 2), Else(Gt("b", 3))),
query: "CASE WHEN (`a` = ?) THEN ? ELSE `b` > ? END",
value: []interface{}{1, 2, 3},
},
{
caseBuilder: Case(When(Eq("col", "a"), 1), When(Eq("col", "b"), 2), Else(3)),
query: "CASE WHEN (`col` = ?) THEN ? WHEN (`col` = ?) THEN ? ELSE ? END",
value: []interface{}{"a", 1, "b", 2, 3},
},
{
caseBuilder: Case(When(Eq("colA", "a"), Lt("colB", 5)), When(Eq("colA", "b"), Lt("colB", 10)), Else(Lt("colB", 15))),
query: "CASE WHEN (`colA` = ?) THEN `colB` < ? WHEN (`colA` = ?) THEN `colB` < ? ELSE `colB` < ? END",
value: []interface{}{"a", 5, "b", 10, 15},
},
{
caseBuilder: Case(When(Eq("colA", "a"), Lt("colB", 5)), When(Eq("colA", "b"), Lt("colB", 10)), Else(Lt("colB", 15))).As("c"),
query: "CASE WHEN (`colA` = ?) THEN `colB` < ? WHEN (`colA` = ?) THEN `colB` < ? ELSE `colB` < ? END AS `c`",
value: []interface{}{"a", 5, "b", 10, 15},
},
} {
buf := NewBuffer()
err := test.caseBuilder.Build(dialect.Clickhouse, buf)
require.NoError(t, err)
require.Equal(t, test.query, buf.String())
require.Equal(t, test.value, buf.Value())
}
}
13 changes: 1 addition & 12 deletions comments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"testing"

"github.com/gocraft/dbr/v2/dialect"
"github.com/stretchr/testify/require"
)

Expand All @@ -30,18 +29,8 @@ func TestComments(t *testing.T) {
expect: "/* test nested comment removed */\n",
},
} {

for _, sess := range testSession {
name := ""
switch sess.Dialect {
case dialect.MySQL:
name = "MySQL"
case dialect.PostgreSQL:
name = "PostgreSQL"
case dialect.SQLite3:
name = "SQLite3"
}
t.Run(fmt.Sprintf("%s/%s", name, test.name), func(t *testing.T) {
t.Run(fmt.Sprintf("%s/%s", testSessionName(sess), test.name), func(t *testing.T) {
buf := NewBuffer()
err := test.comments.Build(sess.Dialect, buf)
require.NoError(t, err)
Expand Down
2 changes: 2 additions & 0 deletions dbr.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func Open(driver, dsn string, log EventReceiver) (*Connection, error) {
}
var d Dialect
switch driver {
case "clickhouse":
d = dialect.Clickhouse
case "mysql":
d = dialect.MySQL
case "postgres", "pgx":
Expand Down
Loading