Skip to content

Commit

Permalink
Merge branch 'main' into fix/url-history-issues
Browse files Browse the repository at this point in the history
  • Loading branch information
AdityaHegde committed Dec 10, 2024
2 parents fbe200d + 1b1dbdb commit fc5c5f8
Show file tree
Hide file tree
Showing 31 changed files with 619 additions and 374 deletions.
1 change: 1 addition & 0 deletions proto/gen/rill/runtime/v1/runtime.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3099,6 +3099,7 @@ definitions:
- CODE_FLOAT32
- CODE_FLOAT64
- CODE_TIMESTAMP
- CODE_INTERVAL
- CODE_DATE
- CODE_TIME
- CODE_STRING
Expand Down
88 changes: 46 additions & 42 deletions proto/gen/rill/runtime/v1/schema.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions proto/rill/runtime/v1/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ message Type {
CODE_FLOAT32 = 12;
CODE_FLOAT64 = 13;
CODE_TIMESTAMP = 14;
CODE_INTERVAL = 27;
CODE_DATE = 15;
CODE_TIME = 16;
CODE_STRING = 17;
Expand Down
2 changes: 2 additions & 0 deletions runtime/drivers/clickhouse/olap.go
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,8 @@ func databaseTypeToPB(dbt string, nullable bool) (*runtimev1.Type, error) {
t.Code = runtimev1.Type_CODE_TIMESTAMP
case "DATETIME64":
t.Code = runtimev1.Type_CODE_TIMESTAMP
case "INTERVALNANOSECOND", "INTERVALMICROSECOND", "INTERVALMILLISECOND", "INTERVALSECOND", "INTERVALMINUTE", "INTERVALHOUR", "INTERVALDAY", "INTERVALWEEK", "INTERVALMONTH", "INTERVALQUARTER", "INTERVALYEAR":
t.Code = runtimev1.Type_CODE_INTERVAL
case "JSON":
t.Code = runtimev1.Type_CODE_JSON
case "UUID":
Expand Down
62 changes: 42 additions & 20 deletions runtime/drivers/clickhouse/olap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,42 @@ import (
"github.com/stretchr/testify/assert"
"testing"

runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
"github.com/rilldata/rill/runtime/drivers"
"github.com/rilldata/rill/runtime/drivers/clickhouse"
"github.com/rilldata/rill/runtime/pkg/activity"
"github.com/rilldata/rill/runtime/storage"
"github.com/rilldata/rill/runtime/testruntime"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)

func TestClickhouseCrudOps(t *testing.T) {
// t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true")
if testing.Short() {
t.Skip("clickhouse: skipping test in short mode")
}

dsn, cluster := testruntime.ClickhouseCluster(t)
t.Run("SingleHost", func(t *testing.T) { testClickhouseSingleHost(t, dsn) })
t.Run("Cluster", func(t *testing.T) { testClickhouseCluster(t, dsn, cluster) })
}
func TestClickhouseSingle(t *testing.T) {
cfg := testruntime.AcquireConnector(t, "clickhouse")

func testClickhouseSingleHost(t *testing.T, dsn string) {
conn, err := drivers.Open("clickhouse", "default", map[string]any{"dsn": dsn}, storage.MustNew(t.TempDir(), nil), activity.NewNoopClient(), zap.NewNop())
conn, err := drivers.Open("clickhouse", "default", cfg, storage.MustNew(t.TempDir(), nil), activity.NewNoopClient(), zap.NewNop())
require.NoError(t, err)
defer conn.Close()
prepareConn(t, conn)

olap, ok := conn.AsOLAP("default")
require.True(t, ok)
t.Run("RenameView", func(t *testing.T) {
testRenameView(t, olap)
})
t.Run("RenameView", func(t *testing.T) { testRenameView(t, olap) })
t.Run("RenameTable", func(t *testing.T) { testRenameTable(t, olap) })
t.Run("CreateTableAsSelect", func(t *testing.T) { testCreateTableAsSelect(t, olap) })
t.Run("InsertTableAsSelect_WithAppend", func(t *testing.T) { testInsertTableAsSelect_WithAppend(t, olap) })
t.Run("InsertTableAsSelect_WithMerge", func(t *testing.T) { testInsertTableAsSelect_WithMerge(t, olap) })
t.Run("TestDictionary", func(t *testing.T) { testDictionary(t, olap) })

t.Run("TestIntervalType", func(t *testing.T) { testIntervalType(t, olap) })
}

func testClickhouseCluster(t *testing.T, dsn, cluster string) {
func TestClickhouseCluster(t *testing.T) {
if testing.Short() {
t.Skip("clickhouse: skipping test in short mode")
}

dsn, cluster := testruntime.ClickhouseCluster(t)

conn, err := drivers.Open("clickhouse", "default", map[string]any{"dsn": dsn, "cluster": cluster}, storage.MustNew(t.TempDir(), nil), activity.NewNoopClient(), zap.NewNop())
require.NoError(t, err)
defer conn.Close()
Expand All @@ -54,9 +51,7 @@ func testClickhouseCluster(t *testing.T, dsn, cluster string) {

prepareClusterConn(t, olap, cluster)

t.Run("RenameView", func(t *testing.T) {
testRenameView(t, olap)
})
t.Run("RenameView", func(t *testing.T) { testRenameView(t, olap) })
t.Run("RenameTable", func(t *testing.T) { testRenameTable(t, olap) })
t.Run("CreateTableAsSelect", func(t *testing.T) { testCreateTableAsSelect(t, olap) })
t.Run("InsertTableAsSelect_WithAppend", func(t *testing.T) { testInsertTableAsSelect_WithAppend(t, olap) })
Expand Down Expand Up @@ -271,6 +266,33 @@ func testDictionary(t *testing.T, olap drivers.OLAPStore) {
require.NoError(t, olap.DropTable(context.Background(), "dict1", false))
}

func testIntervalType(t *testing.T, olap drivers.OLAPStore) {
cases := []struct {
query string
ms int64
}{
{query: "SELECT INTERVAL '1' SECOND", ms: 1000},
{query: "SELECT INTERVAL '2' MINUTES", ms: 2 * 60 * 1000},
{query: "SELECT INTERVAL '3' HOURS", ms: 3 * 60 * 60 * 1000},
{query: "SELECT INTERVAL '4' DAYS", ms: 4 * 24 * 60 * 60 * 1000},
{query: "SELECT INTERVAL '5' MONTHS", ms: 5 * 30 * 24 * 60 * 60 * 1000},
{query: "SELECT INTERVAL '6' YEAR", ms: 6 * 365 * 24 * 60 * 60 * 1000},
}
for _, c := range cases {
rows, err := olap.Execute(context.Background(), &drivers.Statement{Query: c.query})
require.NoError(t, err)
require.Equal(t, runtimev1.Type_CODE_INTERVAL, rows.Schema.Fields[0].Type.Code)

require.True(t, rows.Next())
var s string
require.NoError(t, rows.Scan(&s))
ms, ok := clickhouse.ParseIntervalToMillis(s)
require.True(t, ok)
require.Equal(t, c.ms, ms)
require.NoError(t, rows.Close())
}
}

func prepareClusterConn(t *testing.T, olap drivers.OLAPStore, cluster string) {
err := olap.Exec(context.Background(), &drivers.Statement{
Query: fmt.Sprintf("CREATE OR REPLACE TABLE foo_local ON CLUSTER %s (bar VARCHAR, baz INTEGER) engine=MergeTree ORDER BY tuple()", cluster),
Expand Down
43 changes: 43 additions & 0 deletions runtime/drivers/clickhouse/utils.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
package clickhouse

import (
"strconv"
"strings"

"github.com/rilldata/rill/runtime/drivers"
)

// ParseIntervalToMillis parses a ClickHouse INTERVAL string into milliseconds.
// ClickHouse currently returns INTERVALs as strings in the format "1 Month", "2 Minutes", etc.
// This function follows our current policy of treating months as 30 days when converting to milliseconds.
func ParseIntervalToMillis(s string) (int64, bool) {
s1, s2, ok := strings.Cut(s, " ")
if !ok {
return 0, false
}

units, err := strconv.ParseInt(s1, 10, 64)
if err != nil {
return 0, false
}

switch s2 {
case "Nanosecond", "Nanoseconds":
return int64(float64(units) / 1_000_000), true
case "Microsecond", "Microseconds":
return int64(float64(units) / 1_000), true
case "Millisecond", "Milliseconds":
return units * 1, true
case "Second", "Seconds":
return units * 1000, true
case "Minute", "Minutes":
return units * 60 * 1000, true
case "Hour", "Hours":
return units * 60 * 60 * 1000, true
case "Day", "Days":
return units * 24 * 60 * 60 * 1000, true
case "Month", "Months":
return units * 30 * 24 * 60 * 60 * 1000, true
case "Quarter", "Quarters":
return units * 3 * 30 * 24 * 60 * 60 * 1000, true
case "Year", "Years":
return units * 365 * 24 * 60 * 60 * 1000, true
default:
return 0, false
}
}

func safeSQLName(name string) string {
return drivers.DialectClickHouse.EscapeIdentifier(name)
}
Loading

0 comments on commit fc5c5f8

Please sign in to comment.